diff --git a/extras/amalgamator/Source/Main.cpp b/extras/amalgamator/Source/Main.cpp index 2e7d50b7cd..bc7dd4e7d8 100644 --- a/extras/amalgamator/Source/Main.cpp +++ b/extras/amalgamator/Source/Main.cpp @@ -46,10 +46,10 @@ static bool canFileBeReincluded (const File& f) { 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); + if (content.startsWith ("//")) + content = content.fromFirstOccurrenceOf ("\n", false, false); + else if (content.startsWith ("/*")) + content = content.fromFirstOccurrenceOf ("*/", false, false); else break; } @@ -59,10 +59,10 @@ static bool canFileBeReincluded (const File& f) lines.trim(); lines.removeEmptyStrings(); - const String l1 (lines[0].removeCharacters (T(" \t")).trim()); - const String l2 (lines[1].removeCharacters (T(" \t")).trim()); + const String l1 (lines[0].removeCharacters (" \t").trim()); + const String l2 (lines[1].removeCharacters (" \t").trim()); - if (l1.replace (T("#ifndef"), T("#define")) == l2) + if (l1.replace ("#ifndef", "#define") == l2) return false; return true; @@ -105,7 +105,8 @@ static bool parseFile (const File& rootFolder, StringArray& alreadyIncludedFiles, const StringArray& includesToIgnore, const StringArray& wildcards, - const bool isOuterFile) + bool isOuterFile, + bool stripCommentBlocks) { if (! file.exists()) { @@ -129,26 +130,26 @@ static bool parseFile (const File& rootFolder, String line (lines[i]); String trimmed (line.trimStart()); - if ((! isOuterFile) && trimmed.startsWith (T("//================================================================"))) + if ((! isOuterFile) && trimmed.startsWith ("//================================================================")) line = String::empty; - if (trimmed.startsWithChar (T('#')) - && trimmed.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\""))) + if (trimmed.startsWithChar ('#') + && trimmed.removeCharacters (" \t").startsWithIgnoreCase ("#include\"")) { - const int endOfInclude = line.indexOfChar (line.indexOfChar (T('\"')) + 1, T('\"')) + 1; + const int endOfInclude = line.indexOfChar (line.indexOfChar ('\"') + 1, '\"') + 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 String filename (line.fromFirstOccurrenceOf ("\"", false, false) + .upToLastOccurrenceOf ("\"", false, false)); const File targetFile (file.getSiblingFile (filename)); if (targetFile.exists() && targetFile.isAChildOf (rootFolder)) { - if (matchesWildcard (filename.replaceCharacter (T('\\'), T('/')), wildcards) + if (matchesWildcard (filename.replaceCharacter ('\\', '/'), wildcards) && ! includesToIgnore.contains (targetFile.getFileName())) { - if (line.containsIgnoreCase (T("FORCE_AMALGAMATOR_INCLUDE")) + if (line.containsIgnoreCase ("FORCE_AMALGAMATOR_INCLUDE") || ! alreadyIncludedFiles.contains (targetFile.getFullPathName())) { if (! canFileBeReincluded (targetFile)) @@ -158,7 +159,7 @@ static bool parseFile (const File& rootFolder, if (! parseFile (rootFolder, newTargetFile, dest, targetFile, alreadyIncludedFiles, includesToIgnore, - wildcards, false)) + wildcards, false, stripCommentBlocks)) { return false; } @@ -174,23 +175,23 @@ static bool parseFile (const File& rootFolder, } else { - line = lineUpToEndOfInclude.upToFirstOccurrenceOf (T("\""), true, false) + line = lineUpToEndOfInclude.upToFirstOccurrenceOf ("\"", true, false) + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory()) - .replaceCharacter (T('\\'), T('/')) - + T("\"") + .replaceCharacter ('\\', '/') + + "\"" + lineAfterInclude; } } } - if (trimmed.startsWith (T("/*")) && (i > 10 || ! isOuterFile)) + if ((stripCommentBlocks || i == 0) && trimmed.startsWith ("/*") && (i > 10 || ! isOuterFile)) { int originalI = i; String originalLine = line; for (;;) { - int end = line.indexOf (T("*/")); + int end = line.indexOf ("*/"); if (end >= 0) { @@ -198,8 +199,8 @@ static bool parseFile (const File& rootFolder, // If our comment appeared just before an assertion, leave it in, as it // might be useful.. - if (lines [i + 1].contains (T("assert")) - || lines [i + 2].contains (T("assert"))) + if (lines [i + 1].contains ("assert") + || lines [i + 2].contains ("assert")) { i = originalI; line = originalLine; @@ -232,15 +233,15 @@ static bool parseFile (const File& rootFolder, { int tabSize = 4; int numTabs = numIntialSpaces / tabSize; - line = String::repeatedString (T("\t"), numTabs) + line.substring (numTabs * tabSize); + line = String::repeatedString ("\t", numTabs) + line.substring (numTabs * tabSize); } - if (! line.containsChar (T('"'))) + if (! line.containsChar ('"')) { // turn large areas of spaces into tabs - this will mess up alignment a bit, but // it's only the amalgamated file, so doesn't matter... - line = line.replace (T(" "), T("\t"), false); - line = line.replace (T(" "), T("\t"), false); + line = line.replace (" ", "\t", false); + line = line.replace (" ", "\t", false); } } @@ -264,7 +265,7 @@ static bool munge (const File& templateFile, const File& targetFile, const Strin } StringArray wildcards; - wildcards.addTokens (wildcard, T(";,"), T("'\"")); + wildcards.addTokens (wildcard, ";,", "'\""); wildcards.trim(); wildcards.removeEmptyStrings(); @@ -286,7 +287,7 @@ static bool munge (const File& templateFile, const File& targetFile, const Strin alreadyIncludedFiles, includesToIgnore, wildcards, - true)) + true, false)) { return false; } @@ -318,17 +319,17 @@ static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alread { String line (lines[i]); - if (line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\""))) + if (line.removeCharacters (" \t").startsWithIgnoreCase ("#include\"")) { - const String filename (line.fromFirstOccurrenceOf (T("\""), false, false) - .upToLastOccurrenceOf (T("\""), false, false)); + const String filename (line.fromFirstOccurrenceOf ("\"", false, false) + .upToLastOccurrenceOf ("\"", 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()) + if (targetFile.getFileName().containsIgnoreCase ("juce_") && targetFile.exists()) findAllFilesIncludedIn (targetFile, alreadyIncludedFiles); } } @@ -344,11 +345,11 @@ static void mungeJuce (const File& juceFolder) return; } - const File hppTemplate (juceFolder.getChildFile (T("amalgamation/juce_amalgamated_template.h"))); - const File cppTemplate (juceFolder.getChildFile (T("amalgamation/juce_amalgamated_template.cpp"))); + const File hppTemplate (juceFolder.getChildFile ("amalgamation/juce_amalgamated_template.h")); + const File cppTemplate (juceFolder.getChildFile ("amalgamation/juce_amalgamated_template.cpp")); - const File hppTarget (juceFolder.getChildFile (T("juce_amalgamated.h"))); - const File cppTarget (juceFolder.getChildFile (T("juce_amalgamated.cpp"))); + const File hppTarget (juceFolder.getChildFile ("juce_amalgamated.h")); + const File cppTarget (juceFolder.getChildFile ("juce_amalgamated.cpp")); StringArray alreadyIncludedFiles, includesToIgnore; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 120df76270..6a160f9905 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -23,7 +23,18 @@ ============================================================================== */ +/* + 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 __JUCE_JUCEHEADER__ + /* When you add the amalgamated cpp file to your project, you mustn't include it in + a file where you've already included juce.h - just put it inside a file on its own, + possibly with your config flags preceding it, but don't include anything else. */ #error #endif @@ -32,6 +43,18 @@ #ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ #define __JUCE_TARGETPLATFORM_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_WINDOWS, 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 +*/ + #if (defined (_WIN32) || defined (_WIN64)) #define JUCE_WIN32 1 #define JUCE_WINDOWS 1 @@ -66,6 +89,7 @@ #define JUCE_MINGW 1 #endif + /** If defined, this indicates that the processor is little-endian. */ #define JUCE_LITTLE_ENDIAN 1 #define JUCE_INTEL 1 @@ -161,38 +185,75 @@ #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 +/** JUCE_FORCE_DEBUG: 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 to force + it to be true or false. +*/ #ifndef JUCE_FORCE_DEBUG //#define JUCE_FORCE_DEBUG 0 #endif +/** JUCE_LOG_ASSERTIONS: 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 0 #endif +/** JUCE_ASIO: Enables ASIO audio devices (MS Windows only). + Turning this on means that you'll need to have the Steinberg ASIO SDK installed + on your Windows build machine. + + See the comments in the ASIOAudioIODevice class's header file for more + info about this. +*/ #ifndef JUCE_ASIO #define JUCE_ASIO 0 #endif +/** JUCE_WASAPI: Enables WASAPI audio devices (Windows Vista and above). +*/ #ifndef JUCE_WASAPI #define JUCE_WASAPI 0 #endif +/** JUCE_DIRECTSOUND: Enables DirectSound audio (MS Windows only). +*/ #ifndef JUCE_DIRECTSOUND #define JUCE_DIRECTSOUND 1 #endif +/** JUCE_ALSA: Enables ALSA audio devices (Linux only). */ #ifndef JUCE_ALSA #define JUCE_ALSA 1 #endif +/** JUCE_JACK: Enables JACK audio devices (Linux only). */ #ifndef JUCE_JACK #define JUCE_JACK 0 #endif +/** JUCE_QUICKTIME: Enables the QuickTimeMovieComponent class (Mac and Windows). + If you're building on Windows, you'll need to have the Apple QuickTime SDK + installed, and its header files will need to be on your include path. +*/ #if ! (defined (JUCE_QUICKTIME) || JUCE_LINUX || JUCE_IPHONE || (JUCE_WINDOWS && ! JUCE_MSVC)) #define JUCE_QUICKTIME 0 #endif @@ -201,70 +262,132 @@ #undef JUCE_QUICKTIME #endif +/** JUCE_OPENGL: Enables the OpenGLComponent class (available on all platforms). + If you're not using OpenGL, you might want to turn this off to reduce your binary's size. +*/ #ifndef JUCE_OPENGL #define JUCE_OPENGL 1 #endif +/** JUCE_USE_FLAC: Enables the FLAC audio codec classes (available on all platforms). + If your app doesn't need to read FLAC files, you might want to disable this to + reduce the size of your codebase and build time. +*/ #ifndef JUCE_USE_FLAC #define JUCE_USE_FLAC 1 #endif +/** JUCE_USE_OGGVORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms). + If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to + reduce the size of your codebase and build time. +*/ #ifndef JUCE_USE_OGGVORBIS #define JUCE_USE_OGGVORBIS 1 #endif +/** JUCE_USE_CDBURNER: Enables the audio CD reader code (Mac and Windows only). + Unless you're using CD-burning, you should probably turn this flag off to + reduce code size. +*/ #if (! defined (JUCE_USE_CDBURNER)) && ! (JUCE_WINDOWS && ! JUCE_MSVC) #define JUCE_USE_CDBURNER 0 #endif +/** JUCE_USE_CDREADER: Enables the audio CD reader code (Mac and Windows only). + Unless you're using CD-reading, you should probably turn this flag off to + reduce code size. +*/ #ifndef JUCE_USE_CDREADER #define JUCE_USE_CDREADER 0 #endif +/** JUCE_USE_CAMERA: Enables web-cam support using the CameraDevice class (Mac and Windows). +*/ #if (JUCE_QUICKTIME || JUCE_WINDOWS) && ! defined (JUCE_USE_CAMERA) #define JUCE_USE_CAMERA 0 #endif +/** JUCE_ENABLE_REPAINT_DEBUGGING: If this option is turned on, each area of the screen that + gets repainted will flash in a random colour, so that you can check exactly how much and how + often your components are being drawn. +*/ #ifndef JUCE_ENABLE_REPAINT_DEBUGGING #define JUCE_ENABLE_REPAINT_DEBUGGING 0 #endif +/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only). + Unless you specifically want to disable this, it's best to leave this option turned on. +*/ #ifndef JUCE_USE_XINERAMA #define JUCE_USE_XINERAMA 1 #endif +/** JUCE_USE_XSHM: Enables X shared memory for faster rendering on Linux. This is best left + turned on unless you have a good reason to disable it. +*/ #ifndef JUCE_USE_XSHM #define JUCE_USE_XSHM 1 #endif +/** JUCE_USE_XRENDER: Uses XRender to allow semi-transparent windowing on Linux. +*/ #ifndef JUCE_USE_XRENDER #define JUCE_USE_XRENDER 0 #endif +/** JUCE_USE_XCURSOR: Uses XCursor to allow ARGB cursor on Linux. This is best left turned on + unless you have a good reason to disable it. +*/ #ifndef JUCE_USE_XCURSOR #define JUCE_USE_XCURSOR 1 #endif +/** JUCE_PLUGINHOST_VST: Enables the VST audio plugin hosting classes. This requires the + Steinberg VST SDK to be installed on your machine, and should be left turned off unless + you're building a plugin hosting app. + + @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU +*/ #ifndef JUCE_PLUGINHOST_VST #define JUCE_PLUGINHOST_VST 0 #endif +/** JUCE_PLUGINHOST_AU: Enables the AudioUnit plugin hosting classes. This is Mac-only, + of course, and should only be enabled if you're building a plugin hosting app. + + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST +*/ #ifndef JUCE_PLUGINHOST_AU #define JUCE_PLUGINHOST_AU 0 #endif +/** JUCE_ONLY_BUILD_CORE_LIBRARY: Enabling this will avoid including any UI classes in the build. + This should be enabled if you're writing a console application. +*/ #ifndef JUCE_ONLY_BUILD_CORE_LIBRARY #define JUCE_ONLY_BUILD_CORE_LIBRARY 0 #endif +/** JUCE_WEB_BROWSER: This lets you disable the WebBrowserComponent class (Mac and Windows). + If you're not using any embedded web-pages, turning this off may reduce your code size. +*/ #ifndef JUCE_WEB_BROWSER #define JUCE_WEB_BROWSER 1 #endif +/** JUCE_SUPPORT_CARBON: Enabling this allows the Mac code to use old Carbon library functions. + + Carbon isn't required for a normal app, but may be needed by specialised classes like + plugin-hosts, which support older APIs. +*/ #ifndef JUCE_SUPPORT_CARBON #define JUCE_SUPPORT_CARBON 1 #endif +/* JUCE_INCLUDE_ZLIB_CODE: Can be used to disable Juce's embedded 3rd-party zlib code. + You might need to tweak this if you're linking to an external zlib library in your app, + but for normal apps, this option should be left alone. +*/ #ifndef JUCE_INCLUDE_ZLIB_CODE #define JUCE_INCLUDE_ZLIB_CODE 1 #endif @@ -285,10 +408,17 @@ #define JUCE_INCLUDE_JPEGLIB_CODE 1 #endif +/** JUCE_CHECK_MEMORY_LEAKS: Enables a memory-leak check when an app terminates. + (Currently, this only affects Windows builds in debug mode). +*/ #ifndef JUCE_CHECK_MEMORY_LEAKS #define JUCE_CHECK_MEMORY_LEAKS 1 #endif +/** JUCE_CATCH_UNHANDLED_EXCEPTIONS: Turn on juce's internal catching of exceptions + that are thrown by the message dispatch loop. With it enabled, any unhandled exceptions + are passed to the JUCEApplication::unhandledException() callback for logging. +*/ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 #endif @@ -393,17 +523,62 @@ #undef PACKED #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). + + If you're compiling and you get an error here 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, and rebuild your Juce library. + */ #include "iasiodrv.h" #endif #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 #if JUCE_USE_CAMERA + /* If you're using the camera classes, you'll need access to a few DirectShow headers. + + These files are provided in the normal Windows SDK, but some Microsoft plonker + didn't realise that qedit.h doesn't actually compile without the rest of the DirectShow SDK.. + Microsoft's suggested fix for this is to hack their qedit.h file! See: + http://social.msdn.microsoft.com/Forums/en-US/windowssdk/thread/ed097d2c-3d68-4f48-8448-277eaaf68252 + .. which is a bit of a bodge, but a lot less hassle than installing the full DShow SDK. + + An alternative workaround is to create a dummy dxtrans.h file and put it in your include path. + The dummy file just needs to contain the following content: + #define __IDxtCompositor_INTERFACE_DEFINED__ + #define __IDxtAlphaSetter_INTERFACE_DEFINED__ + #define __IDxtJpeg_INTERFACE_DEFINED__ + #define __IDxtKey_INTERFACE_DEFINED__ + ..and that should be enough to convince qedit.h that you have the SDK! + */ #include #include #include @@ -419,12 +594,22 @@ #if JUCE_QUICKTIME + /* 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 JUCE_QUICKTIME + flag in juce_Config.h + */ #include #include #include #include #include + /* 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 #endif @@ -433,6 +618,9 @@ #pragma warning (pop) #endif +/** A simple COM smart pointer. + Avoids having to include ATL just to get one of these. +*/ template class ComSmartPtr { @@ -471,6 +659,8 @@ private: ComClass* p; }; +/** Handy base class for writing COM objects, providing ref-counting and a basic QueryInterface method. +*/ template class ComBaseClassHelper : public ComClass { @@ -504,6 +694,13 @@ protected: #ifndef __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__ #define __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__ +/* + This file wraps together all the linux-specific headers, so + that we can include them all just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the + way of the rest of the codebase. +*/ + #include #include #include @@ -529,6 +726,9 @@ protected: #include #include +/* 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 @@ -541,6 +741,7 @@ protected: #include #if JUCE_USE_XINERAMA + /* If you're trying to use Xinerama, you'll need to install the "libxinerama-dev" package.. */ #include #endif @@ -562,16 +763,40 @@ protected: #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 #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 #endif #if JUCE_JACK + /* Got an include error here? If so, you've either not got jack-audio-connection-kit + installed, or you've not got your paths set up correctly to find its header files. + + The package you need to install to get JACK support is "libjack-dev". + + If you don't have the jack-audio-connection-kit library and don't want to build + Juce with low latency audio support, just disable the JUCE_JACK flag in juce_Config.h + */ #include //#include #endif @@ -588,6 +813,13 @@ protected: #ifndef __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ #define __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + #define USE_COREGRAPHICS_RENDERING 1 #if JUCE_IPHONE @@ -670,6 +902,12 @@ protected: #ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ #define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ +/** + Creates a floating carbon window that can be used to hold a carbon UI. + + This is a handy class that's designed to be inlined where needed, e.g. + in the audio plugin hosting code. +*/ class CarbonViewWrapperComponent : public Component, public ComponentMovementWatcher, public Timer @@ -13667,6 +13905,39 @@ XmlElement::~XmlElement() throw() namespace XmlOutputFunctions { + /*static bool isLegalXmlCharSlow (const juce_wchar character) throw() + { + 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 generateLegalCharConstants() + { + uint8 n[32]; + zerostruct (n); + for (int i = 0; i < 256; ++i) + if (isLegalXmlCharSlow (i)) + n[i >> 3] |= (1 << (i & 7)); + + String s; + for (int i = 0; i < 32; ++i) + s << (int) n[i] << ", "; + + DBG (s); + }*/ static bool isLegalXmlChar (const uint32 c) throw() { @@ -20867,6 +21138,12 @@ END_JUCE_NAMESPACE #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 @@ -31095,6 +31372,12 @@ BEGIN_JUCE_NAMESPACE #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 @@ -31115,6 +31398,11 @@ BEGIN_JUCE_NAMESPACE #ifndef __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ #define __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ +/** Holds a set of VSTMidiEvent objects and makes it easy to add + events to the list. + + This is used by both the VST hosting code and the plugin wrapper. +*/ class VSTMidiEventList { public: @@ -31811,6 +32099,10 @@ public: #endif }; +/** + An instance of a plugin, created by a VSTPluginFormat. + +*/ class VSTPluginInstance : public AudioPluginInstance, private Timer, private AsyncUpdater @@ -32038,6 +32330,16 @@ void VSTPluginInstance::initialise() // 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()); @@ -34936,6 +35238,9 @@ private: 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: @@ -37528,12 +37833,24 @@ bool MessageManager::currentThreadHasLockedMessageManager() const throw() return thisThread == messageThreadId || thisThread == threadWithLock; } +/* The only safe way to lock the message thread while another thread does + some work is by posting a special message, whose purpose is to tie up the event + loop until the other thread has finished its business. + + Any other approach can get horribly deadlocked if the OS uses its own hidden locks which + get locked before making an event callback, because if the same OS lock gets indirectly + accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens + in Cocoa). +*/ class MessageManagerLock::SharedEvents : public ReferenceCountedObject { public: SharedEvents() {} ~SharedEvents() {} + /* This class just holds a couple of events to communicate between the BlockingMessage + and the MessageManagerLock. Because both of these objects may be deleted at any time, + this shared data must be kept in a separate, ref-counted container. */ WaitableEvent lockedEvent, releaseEvent; private: @@ -37810,10 +38127,19 @@ public: if (timeUntilFirstTimer <= 0) { + /* If we managed to set the atomic boolean to true then send a message, this is needed + as a memory barrier so the message won't be sent before callbackNeeded is set to true, + but if it fails it means the message-thread changed the value from under us so at least + some processing is happenening and we can just loop around and try again + */ if (callbackNeeded.compareAndSetBool (1, 0)) { postMessage (new Message()); + /* Sometimes our message can get discarded by the OS (e.g. 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.get() != 0) @@ -37858,6 +38184,12 @@ public: JUCE_CATCH_EXCEPTION } + /* This is needed as a memory barrier to make sure all processing of current timers is done + before the boolean is set. This set should never fail since if it was false in the first place, + we wouldn't get a message (so it can't be changed from false to true from under us), and if we + get a message then the value is true and the other thread can only set it to true again and + we will get another callback to set it to false. + */ callbackNeeded.set (0); } @@ -44960,6 +45292,14 @@ void CodeEditorComponent::mouseDown (const MouseEvent& e) } else { + /*PopupMenu m; + addPopupMenuItems (m, &e); + + const int result = m.show(); + + if (result != 0) + performPopupMenuAction (result); + */ } } @@ -77541,6 +77881,8 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_TopLevelWindow.cpp ***/ BEGIN_JUCE_NAMESPACE +/** Keeps track of the active top level window. +*/ class TopLevelWindowManager : public Timer, public DeletedAtShutdown { @@ -91176,6 +91518,12 @@ BEGIN_JUCE_NAMESPACE #ifndef DOXYGEN +/** + Used internally by ImageFileFormat - don't use this class directly in your + application. + + @see ImageFileFormat +*/ class GIFLoader { public: @@ -91762,6 +92110,8 @@ namespace zlibNamespace /*** 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 @@ -91773,6 +92123,10 @@ namespace zlibNamespace #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 @@ -91846,6 +92200,10 @@ namespace zlibNamespace # 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 @@ -91886,10 +92244,12 @@ namespace zlibNamespace # 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 @@ -91898,10 +92258,30 @@ namespace zlibNamespace # 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 @@ -91910,8 +92290,15 @@ namespace zlibNamespace # 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 @@ -91920,6 +92307,7 @@ namespace zlibNamespace # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far @@ -91930,6 +92318,9 @@ namespace zlibNamespace #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 @@ -91939,11 +92330,17 @@ namespace zlibNamespace # 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 @@ -91986,6 +92383,7 @@ 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; @@ -92033,6 +92431,7 @@ typedef uLong FAR uLongf; # endif #endif +/* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) # pragma map(deflateInit_,"DEIN") # pragma map(deflateInit2_,"DEIN2") @@ -92059,6 +92458,40 @@ extern "C" { #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)); @@ -92087,6 +92520,10 @@ typedef struct z_stream_s { 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 */ @@ -92106,12 +92543,47 @@ typedef struct gz_header_s { 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 @@ -92122,85 +92594,698 @@ typedef gz_header FAR *gz_headerp; #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)); @@ -92208,76 +93293,437 @@ 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, @@ -92508,10 +93954,13 @@ namespace zlibNamespace /*** 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); @@ -92519,6 +93968,7 @@ namespace zlibNamespace #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 { \ @@ -92553,14 +94003,17 @@ namespace zlibNamespace # 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) @@ -92571,9 +94024,11 @@ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) 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++; @@ -92585,6 +94040,7 @@ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) 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 */ @@ -92596,6 +94052,7 @@ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) MOD(sum2); } + /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; @@ -92610,15 +94067,18 @@ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) 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; @@ -92635,8 +94095,21 @@ uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) /*** 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) { @@ -92646,6 +94119,7 @@ int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, 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; @@ -92670,11 +94144,17 @@ int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, 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; @@ -92685,6 +94165,16 @@ uLong ZEXPORT compressBound (uLong sourceLen) #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 @@ -92694,6 +94184,13 @@ uLong ZEXPORT compressBound (uLong sourceLen) /*** 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 @@ -92708,6 +94205,11 @@ uLong ZEXPORT compressBound (uLong sourceLen) #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; @@ -92720,6 +94222,7 @@ uLong ZEXPORT compressBound (uLong sourceLen) #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; @@ -92728,35 +94231,46 @@ 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 @@ -92829,6 +94343,8 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif + /* common defaults */ + #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif @@ -92837,6 +94353,8 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define F_OPEN(name, mode) fopen((name), (mode)) #endif + /* functions */ + #if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF @@ -92849,12 +94367,15 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #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 @@ -92871,6 +94392,10 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # 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) @@ -92892,6 +94417,7 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ extern void zmemzero OF((Bytef* dest, uInt len)); #endif +/* Diagnostic functions */ #ifdef DEBUG # include extern int z_verbose; @@ -92926,6 +94452,7 @@ void zcfree OF((voidpf opaque, voidpf ptr)); #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 @@ -92946,6 +94473,7 @@ void zcfree OF((voidpf opaque, voidpf ptr)); # 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)) @@ -92958,6 +94486,7 @@ void zcfree OF((voidpf opaque, voidpf ptr)); # 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)); @@ -92970,21 +94499,53 @@ 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++) @@ -92993,6 +94554,8 @@ local void make_crc_table() } #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); @@ -93007,11 +94570,13 @@ local void make_crc_table() 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; @@ -93050,6 +94615,9 @@ local void write_table(out, table) #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] = @@ -93494,6 +95062,9 @@ local const unsigned long FAR crc_table[TBLS][256] = #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 @@ -93503,9 +95074,11 @@ const unsigned long FAR * ZEXPORT get_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; @@ -93539,11 +95112,13 @@ unsigned long ZEXPORT crc32 (unsigned long crc, const unsigned char FAR *buf, un #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; @@ -93574,11 +95149,13 @@ local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf 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; @@ -93615,6 +95192,7 @@ local unsigned long crc32_big (unsigned long crc, const unsigned char FAR *buf, #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; @@ -93629,6 +95207,7 @@ local unsigned long gf2_matrix_times (unsigned long *mat, unsigned long vec) return sum; } +/* ========================================================================= */ local void gf2_matrix_square (unsigned long *square, unsigned long *mat) { int n; @@ -93637,6 +95216,7 @@ local void gf2_matrix_square (unsigned long *square, unsigned long *mat) square[n] = gf2_matrix_times(mat, mat[n]); } +/* ========================================================================= */ uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) { int n; @@ -93644,9 +95224,11 @@ uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) 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++) { @@ -93654,26 +95236,35 @@ uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) 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; } @@ -93682,30 +95273,98 @@ uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) /*** 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 @@ -93714,7 +95373,9 @@ uLong ZEXPORT crc32_combine (uLong crc1, uLong crc2, z_off_t len2) #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 */ @@ -93743,6 +95404,10 @@ 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 */ @@ -93756,15 +95421,32 @@ typedef struct internal_state { 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. */ @@ -93774,8 +95456,16 @@ typedef struct internal_state { 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 */ @@ -93785,19 +95475,37 @@ typedef struct internal_state { 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 */ @@ -93807,20 +95515,48 @@ typedef struct internal_state { 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 */ @@ -93833,16 +95569,32 @@ typedef struct internal_state { #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, @@ -93853,8 +95605,13 @@ void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, #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[]; @@ -93892,7 +95649,16 @@ void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, 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 */ @@ -93901,6 +95667,7 @@ typedef enum { } 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)); @@ -93927,14 +95694,28 @@ 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 */ @@ -93945,31 +95726,55 @@ typedef struct config_s { #ifdef FASTEST local const config configuration_table[2] = { - {0, 0, 0, 0, deflate_stored}, /* store only */ - {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +/* 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] = { - {0, 0, 0, 0, deflate_stored}, /* store only */ - {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ - {4, 5, 16, 8, deflate_fast}, - {4, 6, 32, 32, deflate_fast}, +/* 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, 16, 16, deflate_slow}, /* lazy matches */ - {8, 16, 32, 32, deflate_slow}, - {8, 16, 128, 128, deflate_slow}, - {8, 32, 128, 256, deflate_slow}, - {32, 128, 258, 1024, deflate_slow}, - {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +/* 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)]), \ @@ -93982,16 +95787,23 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ 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; @@ -93999,6 +95811,9 @@ int ZEXPORT deflateInit2_ (z_streamp strm, int level, int method, int windowB 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)) { @@ -94078,6 +95893,7 @@ int ZEXPORT deflateInit2_ (z_streamp strm, int level, int method, int windowB return deflateReset(strm); } +/* ========================================================================= */ int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt dictLength) { deflate_state *s; @@ -94103,6 +95919,10 @@ int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt 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++) { @@ -94112,6 +95932,7 @@ int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt return Z_OK; } +/* ========================================================================= */ int ZEXPORT deflateReset (z_streamp strm) { deflate_state *s; @@ -94146,6 +95967,7 @@ int ZEXPORT deflateReset (z_streamp strm) return Z_OK; } +/* ========================================================================= */ int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head) { if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; @@ -94154,6 +95976,7 @@ int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp 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; @@ -94162,6 +95985,7 @@ int ZEXPORT deflatePrime (z_streamp strm, int bits, int value) return Z_OK; } +/* ========================================================================= */ int ZEXPORT deflateParams (z_streamp strm, int level, int strategy) { deflate_state *s; @@ -94182,6 +96006,7 @@ int ZEXPORT deflateParams (z_streamp strm, int level, int strategy) 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) { @@ -94195,6 +96020,7 @@ int ZEXPORT deflateParams (z_streamp strm, int level, int strategy) return err; } +/* ========================================================================= */ int ZEXPORT deflateTune (z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain) { deflate_state *s; @@ -94208,30 +96034,62 @@ int ZEXPORT deflateTune (z_streamp strm, int good_length, int max_lazy, int nice 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; @@ -94250,6 +96108,7 @@ local void flush_pending (z_streamp strm) } } +/* ========================================================================= */ int ZEXPORT deflate (z_streamp strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ @@ -94272,6 +96131,7 @@ int ZEXPORT deflate (z_streamp strm, int flush) old_flush = s->last_flush; s->last_flush = flush; + /* Write the header */ if (s->status == INIT_STATE) { #ifdef GZIP if (s->wrap == 2) { @@ -94338,6 +96198,7 @@ int ZEXPORT deflate (z_streamp strm, int flush) 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)); @@ -94450,22 +96311,36 @@ int ZEXPORT deflate (z_streamp strm, int flush) } #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; @@ -94480,12 +96355,22 @@ int ZEXPORT deflate (z_streamp strm, int flush) 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 */ } @@ -94502,6 +96387,7 @@ int ZEXPORT deflate (z_streamp strm, int flush) 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)); @@ -94520,10 +96406,14 @@ int ZEXPORT deflate (z_streamp strm, int flush) 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; @@ -94541,6 +96431,7 @@ int ZEXPORT deflateEnd (z_streamp strm) 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); @@ -94552,6 +96443,11 @@ int ZEXPORT deflateEnd (z_streamp strm) 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 @@ -94586,6 +96482,7 @@ int ZEXPORT deflateCopy (z_streamp dest, z_streamp source) 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)); @@ -94603,6 +96500,13 @@ int ZEXPORT deflateCopy (z_streamp dest, z_streamp source) #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; @@ -94627,12 +96531,17 @@ local int read_buf (z_streamp strm, Bytef *buf, unsigned size) 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; @@ -94652,7 +96561,19 @@ local void lm_init (deflate_state *s) } #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 */ @@ -94663,10 +96584,16 @@ local uInt longest_match(deflate_state *s, IPos cur_match) 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); @@ -94676,11 +96603,18 @@ local uInt longest_match(deflate_state *s, IPos cur_match) 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"); @@ -94689,10 +96623,30 @@ local uInt longest_match(deflate_state *s, IPos cur_match) 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 { @@ -94701,7 +96655,9 @@ local uInt longest_match(deflate_state *s, IPos cur_match) *(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++; @@ -94715,9 +96671,18 @@ local uInt longest_match(deflate_state *s, IPos cur_match) *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 && @@ -94752,6 +96717,9 @@ local uInt longest_match(deflate_state *s, IPos cur_match) #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 */ @@ -94759,6 +96727,9 @@ local uInt longest_match_fast (deflate_state *s, IPos cur_match) 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"); @@ -94767,11 +96738,22 @@ local uInt longest_match_fast (deflate_state *s, IPos cur_match) 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 && @@ -94790,8 +96772,12 @@ local uInt longest_match_fast (deflate_state *s, IPos cur_match) } #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", @@ -94810,6 +96796,16 @@ local void check_match(deflate_state *s, IPos start, IPos match, int length) # 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; @@ -94820,15 +96816,22 @@ local void fill_window (deflate_state *s) 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); @@ -94836,6 +96839,13 @@ local void fill_window (deflate_state *s) 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 { @@ -94849,17 +96859,32 @@ local void fill_window (deflate_state *s) 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]); @@ -94867,10 +96892,17 @@ local void fill_window (deflate_state *s) 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] : \ @@ -94882,13 +96914,26 @@ local void fill_window (deflate_state *s) 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; @@ -94896,7 +96941,9 @@ local block_state deflate_stored(deflate_state *s, int flush) 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) || @@ -94912,12 +96959,17 @@ local block_state deflate_stored(deflate_state *s, int flush) 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); } @@ -94926,12 +96978,24 @@ local block_state deflate_stored(deflate_state *s, int flush) 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) { @@ -94940,11 +97004,21 @@ local block_state deflate_fast(deflate_state *s, int flush) 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)) { @@ -94957,6 +97031,7 @@ local block_state deflate_fast(deflate_state *s, int flush) 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); @@ -94966,6 +97041,9 @@ local block_state deflate_fast(deflate_state *s, int flush) 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) { @@ -94973,6 +97051,9 @@ local block_state deflate_fast(deflate_state *s, int flush) 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 @@ -94985,8 +97066,12 @@ local block_state deflate_fast(deflate_state *s, int flush) #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--; @@ -94999,12 +97084,23 @@ local block_state deflate_fast(deflate_state *s, int flush) } #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) { @@ -95013,20 +97109,30 @@ local block_state deflate_slow(deflate_state *s, int flush) 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 @@ -95035,17 +97141,29 @@ local block_state deflate_slow(deflate_state *s, int flush) #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 { @@ -95060,6 +97178,10 @@ local block_state deflate_slow(deflate_state *s, int flush) 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) { @@ -95069,6 +97191,9 @@ local block_state deflate_slow(deflate_state *s, int flush) 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--; @@ -95086,6 +97211,11 @@ local block_state deflate_slow(deflate_state *s, int flush) #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; @@ -95097,6 +97227,10 @@ local block_state deflate_rle(s, flush) 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) { @@ -95105,6 +97239,7 @@ local block_state deflate_rle(s, flush) 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; @@ -95116,12 +97251,14 @@ local block_state deflate_rle(s, flush) } 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--; @@ -95139,18 +97276,50 @@ local block_state deflate_rle(s, flush) /*** Start of inlined file: inffast.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, @@ -95166,13 +97335,23 @@ extern int inflate_table OF((codetype type, unsigned short FAR *lens, /*** 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) */ @@ -95206,6 +97385,30 @@ typedef enum { 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 */ @@ -95216,20 +97419,26 @@ struct inflate_state { 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 */ @@ -95245,11 +97454,27 @@ struct inflate_state { /*** 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)++ @@ -95258,6 +97483,41 @@ void inflate_fast OF((z_streamp strm, unsigned start)); # 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; @@ -95281,10 +97541,12 @@ void inflate_fast (z_streamp strm, unsigned start) 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); @@ -95305,6 +97567,8 @@ void inflate_fast (z_streamp strm, unsigned start) 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; @@ -95472,11 +97736,13 @@ void inflate_fast (z_streamp strm, unsigned start) } } 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)); @@ -95487,6 +97753,20 @@ void inflate_fast (z_streamp strm, unsigned start) 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 ***/ @@ -95499,8 +97779,90 @@ void inflate_fast (z_streamp strm, unsigned start) #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 ***/ @@ -95510,6 +97872,7 @@ void inflate_fast OF((z_streamp strm, unsigned start)); # endif #endif +/* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, unsigned out)); #ifdef BUILDFIXED @@ -95599,6 +97962,16 @@ 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 @@ -95606,10 +97979,12 @@ local void fixedtables (struct inflate_state FAR *state) 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; @@ -95620,17 +97995,24 @@ local void fixedtables (struct inflate_state FAR *state) 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 ***/ + /* 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}, @@ -95729,6 +98111,24 @@ local void fixedtables (struct inflate_state FAR *state) #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; @@ -95769,6 +98169,20 @@ void makefixed() } #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; @@ -95776,6 +98190,7 @@ local int updatewindow (z_streamp strm, unsigned out) 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, @@ -95783,12 +98198,14 @@ local int updatewindow (z_streamp strm, unsigned out) 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); @@ -95814,6 +98231,9 @@ local int updatewindow (z_streamp strm, unsigned out) 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)) @@ -95821,6 +98241,7 @@ local int updatewindow (z_streamp strm, unsigned out) # define UPDATE(check, buf, len) adler32(check, buf, len) #endif +/* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ @@ -95839,6 +98260,7 @@ local int updatewindow (z_streamp strm, unsigned out) } while (0) #endif +/* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ @@ -95849,6 +98271,7 @@ local int updatewindow (z_streamp strm, unsigned out) bits = state->bits; \ } while (0) +/* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ @@ -95859,12 +98282,15 @@ local int updatewindow (z_streamp strm, unsigned out) 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; \ @@ -95873,31 +98299,119 @@ local int updatewindow (z_streamp strm, unsigned out) 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; @@ -96267,8 +98781,10 @@ int ZEXPORT inflate (z_streamp strm, int flush) } } + /* 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; @@ -96467,6 +98983,12 @@ int ZEXPORT inflate (z_streamp strm, int flush) 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)) @@ -96507,11 +99029,13 @@ int ZEXPORT inflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt 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); @@ -96519,6 +99043,7 @@ int ZEXPORT inflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt return Z_DATA_ERROR; } + /* copy dictionary to window */ if (updatewindow(strm, strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; @@ -96542,15 +99067,28 @@ 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; @@ -96578,10 +99116,12 @@ int ZEXPORT inflateSync (z_streamp strm) 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; @@ -96596,11 +99136,13 @@ int ZEXPORT inflateSync (z_streamp strm) 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); @@ -96609,6 +99151,14 @@ int ZEXPORT inflateSync (z_streamp strm) 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; @@ -96625,11 +99175,13 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) 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; @@ -96643,6 +99195,7 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) } } + /* copy state */ zmemcpy(dest, source, sizeof(z_stream)); zmemcpy(copy, state, sizeof(struct inflate_state)); if (state->lencode >= state->codes && @@ -96668,7 +99221,25 @@ int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) 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, @@ -96711,11 +99282,44 @@ int inflate_table (codetype type, 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; @@ -96733,6 +99337,7 @@ int inflate_table (codetype type, 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; @@ -96742,13 +99347,48 @@ int inflate_table (codetype type, 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 */ @@ -96767,6 +99407,7 @@ int inflate_table (codetype type, end = -1; } + /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ @@ -96777,10 +99418,13 @@ int inflate_table (codetype type, 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; @@ -96795,6 +99439,7 @@ int inflate_table (codetype type, 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 */ @@ -96803,6 +99448,7 @@ int inflate_table (codetype type, next[(huff >> drop) + fill] = thisx; } while (fill != 0); + /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; @@ -96813,18 +99459,23 @@ int inflate_table (codetype type, 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) { @@ -96834,10 +99485,12 @@ int inflate_table (codetype type, 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; @@ -96845,10 +99498,18 @@ int inflate_table (codetype type, } } + /* + 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; @@ -96856,8 +99517,10 @@ int inflate_table (codetype type, 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; @@ -96869,6 +99532,7 @@ int inflate_table (codetype type, huff = 0; } + /* set return parameters */ *table += used; *bits = root; return 0; @@ -96877,19 +99541,58 @@ int inflate_table (codetype type, /*** 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}; @@ -96902,24 +99605,50 @@ local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ 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 @@ -97071,6 +99800,10 @@ local static_tree_desc static_d_desc = 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)); @@ -97097,6 +99830,7 @@ local void gen_trees_header OF((void)); #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) \ @@ -97104,11 +99838,19 @@ local void gen_trees_header OF((void)); 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)); @@ -97118,6 +99860,10 @@ local void send_bits (deflate_state *s, int value, int length) 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); @@ -97145,6 +99891,11 @@ local void send_bits (deflate_state *s, int value, int length) } #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) @@ -97155,15 +99906,18 @@ local void tr_static_init() 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; @@ -97172,8 +99926,13 @@ local void tr_static_init() } } Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ _length_code[length-1] = (uch)code; + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; @@ -97191,14 +99950,20 @@ local void tr_static_init() } 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); @@ -97211,6 +99976,9 @@ local void tr_static_init() #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ #ifdef GEN_TREES_H # ifndef DEBUG # include @@ -97269,6 +100037,9 @@ void gen_trees_header() } #endif /* GEN_TREES_H */ +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ void _tr_init(deflate_state *s) { tr_static_init(); @@ -97290,13 +100061,18 @@ void _tr_init(deflate_state *s) 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; @@ -97307,7 +100083,12 @@ local void init_block (deflate_state *s) } #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]; \ @@ -97315,10 +100096,20 @@ local void init_block (deflate_state *s) 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 */ @@ -97326,19 +100117,33 @@ local void pqdownheap (deflate_state *s, 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; @@ -97356,6 +100161,9 @@ local void gen_bitlen (deflate_state *s, tree_desc *desc) 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++) { @@ -97363,6 +100171,7 @@ local void gen_bitlen (deflate_state *s, tree_desc *desc) 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 */ @@ -97376,16 +100185,26 @@ local void gen_bitlen (deflate_state *s, tree_desc *desc) 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) { @@ -97402,6 +100221,14 @@ local void gen_bitlen (deflate_state *s, tree_desc *desc) } } +/* =========================================================================== + * 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 */ @@ -97411,9 +100238,15 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ 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<heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { @@ -97449,16 +100295,28 @@ local void build_tree (deflate_state *s, } } + /* 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 */ @@ -97467,6 +100325,7 @@ local void build_tree (deflate_state *s, 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); @@ -97477,6 +100336,7 @@ local void build_tree (deflate_state *s, 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); @@ -97484,11 +100344,19 @@ local void build_tree (deflate_state *s, 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 */ @@ -97529,6 +100397,10 @@ local void scan_tree (deflate_state *s, } } +/* =========================================================================== + * 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 */ @@ -97541,7 +100413,7 @@ local void send_tree (deflate_state *s, int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ - /* guard already set */ + /* 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++) { @@ -97575,18 +100447,32 @@ local void send_tree (deflate_state *s, } } +/* =========================================================================== + * 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)); @@ -97594,6 +100480,11 @@ local int build_bl_tree (deflate_state *s) 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 */ { @@ -97619,6 +100510,9 @@ local void send_all_trees (deflate_state *s, 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 */ @@ -97629,6 +100523,17 @@ void _tr_stored_block (deflate_state *s, charf *buf, ulg stored_len, int eof) 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); @@ -97637,6 +100542,11 @@ void _tr_align (deflate_state *s) 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); @@ -97648,6 +100558,10 @@ void _tr_align (deflate_state *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 */ @@ -97656,11 +100570,14 @@ void _tr_flush_block (deflate_state *s, 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)); @@ -97668,9 +100585,16 @@ void _tr_flush_block (deflate_state *s, 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; @@ -97689,7 +100613,14 @@ void _tr_flush_block (deflate_state *s, 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 @@ -97712,6 +100643,9 @@ void _tr_flush_block (deflate_state *s, #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) { @@ -97724,6 +100658,10 @@ void _tr_flush_block (deflate_state *s, 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) */ @@ -97731,9 +100669,11 @@ int _tr_tally (deflate_state *s, 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) && @@ -97744,7 +100684,9 @@ int _tr_tally (deflate_state *s, } #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; @@ -97760,8 +100702,15 @@ int _tr_tally (deflate_state *s, } #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 */ @@ -97779,6 +100728,7 @@ local void compress_block (deflate_state *s, 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]; @@ -97798,6 +100748,7 @@ local void compress_block (deflate_state *s, } } /* 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"); @@ -97807,6 +100758,12 @@ local void compress_block (deflate_state *s, 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; @@ -97821,6 +100778,11 @@ local void set_data_type (deflate_state *s) 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; @@ -97831,6 +100793,9 @@ local unsigned bi_reverse (unsigned code, int len) 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) { @@ -97844,6 +100809,9 @@ local void bi_flush (deflate_state *s) } } +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ local void bi_windup (deflate_state *s) { if (s->bi_valid > 8) { @@ -97858,6 +100826,10 @@ local void bi_windup (deflate_state *s) #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 */ @@ -97884,6 +100856,8 @@ local void copy_block(deflate_state *s, /*** 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 @@ -97900,6 +100874,94 @@ const char * const z_errmsg[10] = { "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 @@ -97914,12 +100976,19 @@ void z_error (const char *m) } #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 @@ -97963,10 +101032,18 @@ void zmemzero(dest, len) #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; @@ -97976,12 +101053,21 @@ typedef struct ptr_table_s { } 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; @@ -97991,6 +101077,7 @@ voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) 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; @@ -98004,6 +101091,7 @@ void zcfree (voidpf opaque, voidpf ptr) farfree(ptr); return; } + /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; @@ -98021,6 +101109,7 @@ void zcfree (voidpf opaque, voidpf ptr) #endif /* __TURBOC__ */ #ifdef M_I86 +/* Microsoft C in 16-bit mode */ # define MY_ZCALLOC @@ -98314,6 +101403,29 @@ namespace FlacNamespace #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 @@ -98327,6 +101439,9 @@ namespace FlacNamespace #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 */ @@ -98335,12 +101450,15 @@ namespace FlacNamespace 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 ***/ @@ -98422,24 +101540,137 @@ typedef FLAC__uint8 FLAC__byte; #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; @@ -98449,6 +101680,8 @@ typedef struct { FLAC__IOCallback_Close close; } FLAC__IOCallbacks; +/* \} */ + #ifdef __cplusplus } #endif @@ -98465,72 +101698,200 @@ typedef struct { 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; @@ -98540,8 +101901,12 @@ extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PAR 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 { @@ -98551,6 +101916,9 @@ typedef struct { 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 */ @@ -98558,45 +101926,71 @@ typedef enum { 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 { @@ -98608,6 +102002,13 @@ typedef struct { 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) */ @@ -98617,6 +102018,15 @@ extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; / 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 */ @@ -98624,34 +102034,61 @@ typedef enum { 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 */ @@ -98665,40 +102102,72 @@ extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 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; @@ -98719,12 +102188,21 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_ 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; @@ -98732,27 +102210,56 @@ typedef struct { 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; @@ -98760,6 +102267,8 @@ typedef struct { 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; @@ -98768,30 +102277,49 @@ typedef struct { 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; @@ -98803,16 +102331,28 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS 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; @@ -98822,6 +102362,7 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 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) */ @@ -98847,26 +102388,59 @@ typedef enum { 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; @@ -98879,16 +102453,27 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 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; @@ -98900,32 +102485,158 @@ typedef struct { 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 @@ -98940,270 +102651,2136 @@ FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Pic #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 @@ -99222,185 +104799,1023 @@ FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMet 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, @@ -99414,6 +105829,74 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( 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, @@ -99427,6 +105910,43 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( 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, @@ -99436,6 +105956,47 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( 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, @@ -99445,6 +106006,39 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( 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, @@ -99454,6 +106048,43 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( 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, @@ -99463,22 +106094,211 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( 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 @@ -99497,237 +106317,1716 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco 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 @@ -99736,6 +108035,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder /*** 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); @@ -99744,6 +108044,7 @@ static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) #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 { @@ -99763,6 +108064,331 @@ 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 ***/ @@ -99806,6 +108432,28 @@ 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; @@ -99824,6 +108472,30 @@ unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) 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) { @@ -99979,11 +108651,17 @@ FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); #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); @@ -99991,13 +108669,23 @@ 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); @@ -100031,15 +108719,24 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br); #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 @@ -100049,10 +108746,15 @@ 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 @@ -100062,13 +108764,29 @@ typedef FLAC__uint32 brword; #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[] = { @@ -100099,6 +108817,7 @@ static const unsigned char byte_to_unary_table[] = { #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 @@ -100109,7 +108828,10 @@ static const unsigned char byte_to_unary_table[] = { #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 */ @@ -100152,12 +108874,14 @@ static FLaC__INLINE void crc16_update_word_(FLAC__BitReader *br, brword word) 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); @@ -100167,20 +108891,45 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) 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; @@ -100195,6 +108944,12 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) 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; @@ -100202,10 +108957,25 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) 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; } @@ -100217,6 +108987,12 @@ void FLAC__bitreader_delete(FLAC__BitReader *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); @@ -100302,6 +109078,7 @@ FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) 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) @@ -100334,6 +109111,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA 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 */ @@ -100346,7 +109124,9 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA 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) { @@ -100373,6 +109153,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA 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++; @@ -100380,7 +109161,13 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA } } 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; @@ -100396,8 +109183,10 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA 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; @@ -100428,6 +109217,8 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReade { 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; @@ -100449,6 +109240,10 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReade 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); @@ -100486,6 +109281,7 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u 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; @@ -100493,6 +109289,7 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u } 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++; @@ -100501,6 +109298,7 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u 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; @@ -100518,6 +109316,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F 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; @@ -100526,6 +109325,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F } 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++]; @@ -100553,6 +109353,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F 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; @@ -100610,8 +109411,16 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, 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; @@ -100627,6 +109436,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, *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)) @@ -100644,12 +109454,15 @@ FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsig 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; @@ -100659,20 +109472,28 @@ FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsig 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; @@ -100682,6 +109503,7 @@ FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[ 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; @@ -100712,8 +109534,16 @@ FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[ 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; @@ -100730,8 +109560,13 @@ FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[ 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)) @@ -100739,10 +109574,15 @@ FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[ 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)) @@ -100751,6 +109591,7 @@ break1: } 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) { @@ -100781,8 +109622,13 @@ break1: } } 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; @@ -100796,8 +109642,10 @@ break1: } } break2: + /* compose the value */ *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + /* are we done? */ --nvals; if(nvals == 0) { br->consumed_bits = cbits; @@ -100815,14 +109663,19 @@ break2: 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; @@ -100833,6 +109686,7 @@ break2: 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; @@ -100863,8 +109717,16 @@ break2: 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; @@ -100880,23 +109742,37 @@ break2: 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)) @@ -100906,6 +109782,7 @@ break1: } 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) { @@ -100932,8 +109809,13 @@ break1: } } 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; @@ -100947,8 +109829,10 @@ break1: ucbits -= parameter; + /* compose the value */ *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + /* are we done? */ --nvals; if(nvals == 0) { br->consumed_bits = cbits; @@ -100974,13 +109858,16 @@ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uns 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<> 1)) - 1; else @@ -101013,13 +109902,16 @@ FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *v 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< /* 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); @@ -101210,15 +110111,33 @@ 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); @@ -101254,6 +110173,10 @@ FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); # 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 */ @@ -101275,8 +110198,12 @@ FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); #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); @@ -101289,6 +110216,8 @@ static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t size) 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; @@ -101326,6 +110255,7 @@ 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); @@ -101333,6 +110263,7 @@ needs support for cases where sizeof(size_t) != 4 return 0; } #else +/* better? */ { if(!size1 || !size2) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -101354,6 +110285,7 @@ static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_ 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) @@ -101363,6 +110295,7 @@ static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_ 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)) @@ -101415,6 +110348,7 @@ static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t 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)) @@ -101428,10 +110362,14 @@ static FLaC__INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t #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 @@ -101442,7 +110380,14 @@ typedef FLAC__uint32 bwword; #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) @@ -101453,6 +110398,7 @@ static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword) #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 @@ -101471,6 +110417,7 @@ struct FLAC__BitWriter { 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; @@ -101479,13 +110426,19 @@ static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) 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)); @@ -101498,9 +110451,16 @@ static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) 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; } @@ -101512,6 +110472,12 @@ void FLAC__bitwriter_delete(FLAC__BitWriter *bw) free(bw); } +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) { FLAC__ASSERT(0 != bw); @@ -101608,14 +110574,18 @@ unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *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; @@ -101623,6 +110593,9 @@ FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **bu 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; } @@ -101635,8 +110608,10 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsign 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; @@ -101649,10 +110624,12 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsign 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; @@ -101664,6 +110641,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FL { 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); @@ -101673,6 +110651,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FL 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; @@ -101699,6 +110678,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FL 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)); @@ -101707,6 +110687,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLA 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) && @@ -101718,6 +110699,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FL 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; @@ -101735,6 +110717,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, co { 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; @@ -101759,6 +110742,7 @@ unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter) 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); @@ -101772,6 +110756,7 @@ unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter) FLAC__ASSERT(parameter > 0); + /* fold signed to unsigned */ if(val < 0) uval = (unsigned)(((-(++val)) << 1) + 1); else @@ -101836,6 +110821,7 @@ FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 va 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; @@ -101864,24 +110850,29 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL 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; @@ -101891,6 +110882,7 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL 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 */ @@ -101899,10 +110891,13 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL } 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) { @@ -101917,10 +110912,12 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL 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; @@ -101937,6 +110934,10 @@ break1: 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; @@ -101963,6 +110964,7 @@ FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, uns FLAC__ASSERT(0 != bw->buffer); FLAC__ASSERT(parameter > 0); + /* fold signed to unsigned */ if(val < 0) uval = (unsigned)(((-(++val)) << 1) + 1); else @@ -101984,8 +110986,10 @@ FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, uns return false; } else { + /* write the unary MSBs */ if(!FLAC__bitwriter_write_zeroes(bw, msbs)) return false; + /* write the unary end bit and binary LSBs */ if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, k+1)) return false; } @@ -101996,10 +111000,13 @@ FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, uns d = (1 << (k+1)) - parameter; q = uval / parameter; r = uval - (q * parameter); + /* write the unary MSBs */ if(!FLAC__bitwriter_write_zeroes(bw, q)) return false; + /* write the unary end bit */ if(!FLAC__bitwriter_write_raw_uint32(bw, 1, 1)) return false; + /* write the binary LSBs */ if(r >= d) { if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) return false; @@ -102037,8 +111044,10 @@ FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned u return false; } else { + /* write the unary MSBs */ if(!FLAC__bitwriter_write_zeroes(bw, msbs)) return false; + /* write the unary end bit and binary LSBs */ if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, k+1)) return false; } @@ -102049,10 +111058,13 @@ FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned u d = (1 << (k+1)) - parameter; q = uval / parameter; r = uval - (q * parameter); + /* write the unary MSBs */ if(!FLAC__bitwriter_write_zeroes(bw, q)) return false; + /* write the unary end bit */ if(!FLAC__bitwriter_write_raw_uint32(bw, 1, 1)) return false; + /* write the binary LSBs */ if(r >= d) { if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) return false; @@ -102169,6 +111181,7 @@ FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 v 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 @@ -102253,21 +111266,41 @@ static void sigill_handler (int sig) #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) @@ -102302,6 +111335,9 @@ static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; 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 @@ -102319,6 +111355,7 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) 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; @@ -102354,13 +111391,19 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) 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) @@ -102393,10 +111436,13 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) 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" @@ -102420,6 +111466,7 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) _try { __asm { # if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ _emit 0x0F _emit 0x57 _emit 0xC0 @@ -102435,8 +111482,13 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) # 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 @@ -102459,6 +111511,7 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) 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 @@ -102471,6 +111524,9 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) info->use_asm = false; #endif +/* + * PPC-specific + */ #elif defined FLAC__CPU_PPC info->type = FLAC__CPUINFO_TYPE_PPC; # if !defined FLAC__NO_ASM @@ -102493,6 +111549,8 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) } # 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; @@ -102515,6 +111573,7 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) 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; @@ -102530,6 +111589,9 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) info->use_asm = false; # endif +/* + * unknown CPI + */ #else info->type = FLAC__CPUINFO_TYPE_UNKNOWN; info->use_asm = false; @@ -102565,6 +111627,8 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) # 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, @@ -102600,6 +111664,8 @@ FLAC__byte const FLAC__crc8_table[256] = { 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, @@ -102716,11 +111782,28 @@ unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) #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; @@ -102734,6 +111817,23 @@ extern const FLAC__fixedpoint FLAC__FP_E; #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 @@ -102771,14 +111883,39 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d 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 @@ -102793,6 +111930,12 @@ void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, #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; @@ -102806,12 +111949,22 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__ 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) { @@ -102820,10 +111973,12 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__ } 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) { @@ -102837,9 +111992,21 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__ 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) @@ -102861,12 +112028,22 @@ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, F 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) { @@ -102875,10 +112052,12 @@ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, F } 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) { @@ -102892,9 +112071,21 @@ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, F 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) @@ -102937,6 +112128,9 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d 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); @@ -102970,6 +112164,10 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig 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; @@ -102992,6 +112190,9 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig 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); @@ -102999,6 +112200,7 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig 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); @@ -103138,6 +112340,7 @@ void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, #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 @@ -103150,191 +112353,222 @@ 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] = { { - 0x00000000, - 0x00000001, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 + /* + * 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 }, { - 0x00000000, - 0x00000010, - 0x00000007, - 0x00000003, - 0x00000001, - 0x00000001, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 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 }, { - 0x00000000, - 0x00000100, - 0x0000006a, - 0x00000031, - 0x00000018, - 0x0000000c, - 0x00000006, - 0x00000003, - 0x00000001, - 0x00000001, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 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 }, { - 0x00000000, - 0x00001000, - 0x000006a4, - 0x00000315, - 0x0000017d, - 0x000000bc, - 0x0000005d, - 0x0000002e, - 0x00000017, - 0x0000000c, - 0x00000006, - 0x00000003, - 0x00000001, - 0x00000001, - 0x00000000, - 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 }, { - 0x00000000, - 0x00010000, - 0x00006a40, - 0x00003151, - 0x000017d6, - 0x00000bba, - 0x000005d1, - 0x000002e6, - 0x00000172, - 0x000000b9, - 0x0000005c, - 0x0000002e, - 0x00000017, - 0x0000000c, - 0x00000006, - 0x00000003 + /* + * 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 }, { - 0x00000000, - 0x00100000, - 0x0006a3fe, - 0x00031513, - 0x00017d60, - 0x0000bb9d, - 0x00005d10, - 0x00002e59, - 0x00001721, - 0x00000b8e, - 0x000005c6, - 0x000002e3, - 0x00000171, - 0x000000b9, - 0x0000005c, - 0x0000002e + /* + * 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 }, { - 0x00000000, - 0x01000000, - 0x006a3fe6, - 0x00315130, - 0x0017d605, - 0x000bb9ca, - 0x0005d0fc, - 0x0002e58f, - 0x0001720e, - 0x0000b8d8, - 0x00005c61, - 0x00002e2d, - 0x00001716, - 0x00000b8b, - 0x000005c5, - 0x000002e3 + /* + * 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 }, { - 0x00000000, - 0x10000000, - 0x06a3fe5c, - 0x03151301, - 0x017d6049, - 0x00bb9ca6, - 0x005d0fba, - 0x002e58f7, - 0x001720da, - 0x000b8d87, - 0x0005c60b, - 0x0002e2d7, - 0x00017160, - 0x0000b8ad, - 0x00005c56, - 0x00002e2b + /* + * 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[] = { { - 0x00000000, - FLAC__U64L(0x100000000), - FLAC__U64L(0x6a3fe5c6), - FLAC__U64L(0x31513015), - FLAC__U64L(0x17d60497), - FLAC__U64L(0x0bb9ca65), - FLAC__U64L(0x05d0fba2), - FLAC__U64L(0x02e58f74), - FLAC__U64L(0x01720d9c), - FLAC__U64L(0x00b8d875), - FLAC__U64L(0x005c60aa), - FLAC__U64L(0x002e2d72), - FLAC__U64L(0x00171600), - FLAC__U64L(0x000b8ad2), - FLAC__U64L(0x0005c55d), - FLAC__U64L(0x0002e2ac) + /* + * 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) }, { - 0x00000000, - FLAC__U64L(0x1000000000000), - FLAC__U64L(0x6a3fe5c60429), - FLAC__U64L(0x315130157f7a), - FLAC__U64L(0x17d60496cfbb), - FLAC__U64L(0xbb9ca64ecac), - FLAC__U64L(0x5d0fba187cd), - FLAC__U64L(0x2e58f7441ee), - FLAC__U64L(0x1720d9c06a8), - FLAC__U64L(0xb8d8752173), - FLAC__U64L(0x5c60aa252e), - FLAC__U64L(0x2e2d71b0d8), - FLAC__U64L(0x1716001719), - FLAC__U64L(0xb8ad1de1b), - FLAC__U64L(0x5c55d640d), - FLAC__U64L(0x2e2abcf52) + /* + * 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 @@ -103353,6 +112587,7 @@ FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned p 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; @@ -103415,17 +112650,20 @@ FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned p #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"; @@ -103603,6 +112841,7 @@ FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) 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; @@ -103626,8 +112865,10 @@ FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_S 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) @@ -103636,6 +112877,7 @@ static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLA 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; @@ -103643,8 +112885,10 @@ FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *se 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) { @@ -103666,6 +112910,12 @@ FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *se 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); @@ -103680,6 +112930,7 @@ static FLaC__INLINE unsigned utf8len_(const FLAC__byte *utf8) 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 */ @@ -103764,6 +113015,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte * 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; @@ -103842,6 +113094,7 @@ FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_Cu 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; @@ -103866,6 +113119,9 @@ FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Pic return true; } +/* + * These routines are private to libFLAC + */ unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) { return @@ -103981,8 +113237,31 @@ FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_s #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 @@ -103996,10 +113275,64 @@ void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], u # 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 @@ -104013,6 +113346,21 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__i #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 @@ -104029,9 +113377,32 @@ void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residu #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 */ @@ -104046,9 +113417,11 @@ unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned m #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) @@ -104060,6 +113433,7 @@ void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FL 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; @@ -104067,6 +113441,13 @@ void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_le 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]; @@ -104074,6 +113455,10 @@ void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_le } #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; @@ -104108,11 +113493,13 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o 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]; @@ -104124,10 +113511,12 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o 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; @@ -104144,11 +113533,13 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, 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]); @@ -104157,6 +113548,7 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, } if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ return 2; } else { @@ -104201,6 +113593,10 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, 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; @@ -104271,6 +113667,14 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, u *(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 */ { @@ -104280,6 +113684,11 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, u 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) { @@ -104537,6 +113946,11 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *da 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) { @@ -104783,6 +114197,14 @@ void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, c *(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 */ { @@ -104792,6 +114214,11 @@ void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, c 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) { @@ -105049,6 +114476,11 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_l 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) { @@ -105352,6 +114784,31 @@ unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned m #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]; @@ -105372,14 +114829,48 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const #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; @@ -105500,10 +114991,16 @@ static void byteSwapX16(FLAC__uint32 *buf) #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 */ @@ -105513,12 +115010,14 @@ static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsign 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); @@ -105527,9 +115026,14 @@ static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsign 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; @@ -105544,13 +115048,19 @@ void FLAC__MD5Init(FLAC__MD5Context *ctx) 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 */ @@ -105563,6 +115073,7 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) 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); @@ -105577,6 +115088,9 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) } } +/* + * 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; @@ -105695,10 +115209,14 @@ static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], u } } +/* + * 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) @@ -105762,6 +115280,9 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const #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); @@ -105781,9 +115302,11 @@ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) 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))); @@ -105791,6 +115314,7 @@ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) # 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)) @@ -106010,6 +115534,9 @@ typedef struct FLAC__StreamDecoderProtected { #endif } FLAC__StreamDecoderProtected; +/* + * return the number of input bytes consumed + */ unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); #endif @@ -106020,12 +115547,14 @@ unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecod #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 @@ -106034,8 +115563,20 @@ FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = #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); @@ -106075,6 +115616,12 @@ static FLAC__StreamDecoderTellStatus file_tell_callback_dec (const FLAC__StreamD 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; @@ -106087,9 +115634,13 @@ typedef struct FLAC__StreamDecoderPrivate { 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; @@ -106112,12 +115663,14 @@ typedef struct FLAC__StreamDecoderPrivate { 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; @@ -106127,6 +115680,12 @@ typedef struct FLAC__StreamDecoderPrivate { #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", @@ -106185,6 +115744,11 @@ FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" }; +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) { FLAC__StreamDecoder *decoder; @@ -106272,6 +115836,12 @@ FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) free(decoder); } +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + static FLAC__StreamDecoderInitStatus init_stream_internal_dec( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, @@ -106310,12 +115880,17 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_dec( 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 @@ -106346,6 +115921,8 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_dec( } #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; @@ -106370,6 +115947,7 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_dec( 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; } @@ -106400,7 +115978,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( metadata_callback, error_callback, client_data, -false + /*is_ogg=*/false ); } @@ -106428,7 +116006,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( metadata_callback, error_callback, client_data, -true + /*is_ogg=*/true ); } @@ -106451,6 +116029,11 @@ static FLAC__StreamDecoderInitStatus init_FILE_internal_( 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 */ @@ -106509,6 +116092,11 @@ static FLAC__StreamDecoderInitStatus init_file_internal_( 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); @@ -106559,6 +116147,9 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) 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) { @@ -106568,6 +116159,12 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) } 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; @@ -106612,6 +116209,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecod 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 @@ -106636,6 +116234,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecode 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) @@ -106694,6 +116293,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder 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) @@ -106819,6 +116419,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamD 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)); @@ -106856,14 +116457,21 @@ FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) 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 */ @@ -106882,8 +116490,18 @@ FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) 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; @@ -107049,21 +116667,26 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco 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; @@ -107083,6 +116706,12 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco } } +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); @@ -107091,6 +116720,12 @@ unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecod 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 @@ -107117,11 +116752,19 @@ void set_defaults_dec(FLAC__StreamDecoder *decoder) #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); @@ -107138,6 +116781,8 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne 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); @@ -107150,6 +116795,12 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne } 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; @@ -107158,6 +116809,9 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne 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; @@ -107222,6 +116876,8 @@ FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) 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; @@ -107309,10 +116965,12 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) 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; @@ -107356,6 +117014,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) 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; @@ -107401,6 +117060,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) } 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; @@ -107471,6 +117131,7 @@ FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is 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)) @@ -107492,6 +117153,7 @@ FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_ 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; @@ -107510,7 +117172,9 @@ FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_ 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 */ } @@ -107524,6 +117188,7 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre 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 */ @@ -107539,10 +117204,12 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre 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; @@ -107661,10 +117328,12 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta 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))) { @@ -107677,6 +117346,7 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta } 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))) { @@ -107689,18 +117359,23 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta } 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))) { @@ -107720,8 +117395,10 @@ 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)) @@ -107729,6 +117406,7 @@ FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) 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; @@ -107739,6 +117417,8 @@ 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; @@ -107746,6 +117426,7 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) } } + /* 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 */ @@ -107765,6 +117446,8 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) 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; @@ -107794,6 +117477,7 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL *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); @@ -107806,9 +117490,13 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL 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); @@ -107828,6 +117516,9 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL 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 */ @@ -107838,13 +117529,18 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL 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); @@ -107867,6 +117563,7 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL 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; @@ -107880,6 +117577,7 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL } } 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++) { @@ -107890,9 +117588,11 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL *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; @@ -107902,6 +117602,7 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL 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; @@ -107922,17 +117623,42 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) 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); @@ -108081,11 +117807,14 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) 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)) @@ -108147,6 +117876,7 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) 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; @@ -108157,6 +117887,7 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) 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; @@ -108176,6 +117907,7 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) 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; } } @@ -108211,6 +117943,9 @@ FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsign 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; @@ -108270,6 +118005,7 @@ FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channe subframe->value = x; + /* decode the subframe */ if(do_full_decode) { for(i = 0; i < decoder->private_->frame.header.blocksize; i++) output[i] = x; @@ -108290,12 +118026,14 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, 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; @@ -108313,6 +118051,7 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, return true; } + /* read residual */ switch(subframe->entropy_coding_method.type) { case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: @@ -108323,6 +118062,7 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, 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); @@ -108343,12 +118083,14 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un 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) { @@ -108358,16 +118100,19 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un } 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; @@ -108385,6 +118130,7 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un return true; } + /* read residual */ switch(subframe->entropy_coding_method.type) { case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: @@ -108395,8 +118141,12 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un 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) @@ -108429,6 +118179,7 @@ FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channe 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); @@ -108445,6 +118196,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne 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); @@ -108512,6 +118264,7 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *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) @@ -108521,6 +118274,16 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) 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; @@ -108542,6 +118305,7 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) 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) @@ -108558,9 +118322,20 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) } } 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 @@ -108569,6 +118344,10 @@ FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecode 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: @@ -108581,6 +118360,7 @@ FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecode return FLAC__STREAM_DECODER_READ_STATUS_ABORT; default: FLAC__ASSERT(0); + /* double protection */ return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } } @@ -108597,6 +118377,7 @@ FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, 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; } @@ -108618,7 +118399,9 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder 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]; @@ -108626,9 +118409,11 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder 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); } } @@ -108636,6 +118421,10 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } + /* + * 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) { @@ -108666,34 +118455,59 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s 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 && @@ -108708,6 +118522,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s 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 && @@ -108721,6 +118536,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s 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; @@ -108730,22 +118546,34 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s } 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 */ @@ -108760,14 +118588,24 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s 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 @@ -108779,21 +118617,27 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s 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; @@ -108823,10 +118667,21 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint 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; @@ -108841,23 +118696,32 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint 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; @@ -108872,14 +118736,22 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } 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; } @@ -108889,9 +118761,16 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint 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; @@ -108900,6 +118779,7 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } 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; @@ -109140,6 +119020,15 @@ FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, #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); @@ -109175,7 +119064,16 @@ void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); #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 { @@ -109219,6 +119117,12 @@ static struct CompressionLevels { { 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); @@ -109366,6 +119270,7 @@ static FLAC__bool set_partitioned_rice_( 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[], @@ -109393,6 +119298,12 @@ static FLAC__StreamEncoderTellStatus file_tell_callback_enc(const FLAC__StreamEn 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 */ @@ -109464,6 +119375,7 @@ typedef struct FLAC__StreamEncoderPrivate { 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 @@ -109476,10 +119388,17 @@ typedef struct FLAC__StreamEncoderPrivate { 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; @@ -109498,6 +119417,12 @@ typedef struct FLAC__StreamEncoderPrivate { 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", @@ -109551,8 +119476,24 @@ FLAC_API const char * const FLAC__StreamEncoderTellStatusString[] = { "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; @@ -109658,6 +119599,12 @@ FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) free(encoder); } +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + static FLAC__StreamEncoderInitStatus init_stream_internal_enc( FLAC__StreamEncoder *encoder, FLAC__StreamEncoderReadCallback read_callback, @@ -109722,6 +119669,8 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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) { @@ -109798,6 +119747,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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++) { @@ -109811,6 +119761,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( } } #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++) { @@ -109821,6 +119772,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( } } + /* 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; @@ -109854,6 +119806,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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 && ( @@ -109905,6 +119858,8 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( #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); @@ -109921,7 +119876,11 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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 @@ -109931,6 +119890,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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) { @@ -109966,10 +119926,12 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( } # 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 @@ -109988,6 +119950,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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; } @@ -109996,7 +119959,14 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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))) { @@ -110006,6 +119976,9 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( } 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; @@ -110024,12 +119997,19 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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)) { @@ -110037,9 +120017,13 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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; @@ -110061,12 +120045,28 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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; @@ -110081,10 +120081,14 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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)) { @@ -110092,10 +120096,12 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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; @@ -110118,13 +120124,13 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream( { return init_stream_internal_enc( encoder, -0, + /*read_callback=*/0, write_callback, seek_callback, tell_callback, metadata_callback, client_data, -false + /*is_ogg=*/false ); } @@ -110146,7 +120152,7 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( tell_callback, metadata_callback, client_data, -true + /*is_ogg=*/true ); } @@ -110166,11 +120172,17 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_enc( 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 */ @@ -110187,11 +120199,12 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_enc( file_write_callback_, encoder->private_->file == stdout? 0 : file_seek_callback_enc, encoder->private_->file == stdout? 0 : file_tell_callback_enc, -0, + /*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; } @@ -110237,6 +120250,11 @@ static FLAC__StreamEncoderInitStatus init_file_internal_enc( 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; @@ -110303,6 +120321,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) #endif update_metadata_(encoder); + /* check if an error occurred while updating metadata */ if(encoder->protected_->state != FLAC__STREAM_ENCODER_OK) error = true; } @@ -110345,6 +120364,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncod 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 @@ -110435,7 +120455,9 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncod 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; @@ -110486,6 +120508,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamE return true; } +/*@@@@add to tests*/ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *encoder, const char *specification) { FLAC__ASSERT(0 != encoder); @@ -110598,6 +120621,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncode if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return false; #if 0 + /*@@@ deprecated: */ encoder->protected_->do_escape_coding = value; #else (void)value; @@ -110646,6 +120670,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__St if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return false; #if 0 + /*@@@ deprecated: */ encoder->protected_->rice_parameter_search_dist = value; #else (void)value; @@ -110675,6 +120700,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encod 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; @@ -110695,6 +120721,10 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encod 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); @@ -110942,6 +120972,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c 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' ! */ @@ -110952,11 +120983,13 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c 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) { @@ -110982,11 +121015,19 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder 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++]; @@ -110998,6 +121039,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder 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; @@ -111013,15 +121055,20 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder } 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; @@ -111038,6 +121085,12 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder return true; } +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + void set_defaults_enc(FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); @@ -111182,11 +121235,18 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) 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); @@ -111226,10 +121286,13 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) 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++) { @@ -111281,6 +121344,7 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) break; default: FLAC__ASSERT(0); + /* double protection */ FLAC__window_hann(encoder->private_->window[i], new_blocksize); break; } @@ -111348,11 +121412,15 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const 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) @@ -111361,6 +121429,11 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const 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; @@ -111377,6 +121450,12 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const 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++; @@ -111405,6 +121484,10 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const 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 @@ -111413,6 +121496,7 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const 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)]; @@ -111425,6 +121509,14 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) 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 + @@ -111450,6 +121542,9 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) } } + /* + * Write total samples + */ { const unsigned total_samples_byte_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + @@ -111480,6 +121575,9 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) } } + /* + * Write min/max framesize + */ { const unsigned min_framesize_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + @@ -111505,6 +121603,9 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) } } + /* + * Write seektable + */ if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { unsigned i; @@ -111551,8 +121652,10 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) } #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 + @@ -111571,15 +121674,29 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) 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 + @@ -111603,6 +121720,9 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) 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 + @@ -111632,6 +121752,9 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) memcpy(page.body + total_samples_byte_offset, b, 5); } + /* + * Write min/max framesize + */ { const unsigned min_framesize_offset = FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + @@ -111660,6 +121783,9 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) } 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; @@ -111721,20 +121847,33 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional 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) || @@ -111744,10 +121883,17 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional 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; @@ -111761,6 +121907,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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; } @@ -111770,6 +121919,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } 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; @@ -111778,6 +121930,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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) { @@ -111801,6 +121956,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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); @@ -111817,6 +121975,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } } + /* + * First do a normal encoding pass of each independent channel + */ if(do_independent) { for(channel = 0; channel < encoder->protected_->channels; channel++) { if(! @@ -111838,6 +121999,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } } + /* + * Now do mid and side channels if requested + */ if(do_mid_side) { FLAC__ASSERT(encoder->protected_->channels == 2); @@ -111861,6 +122025,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } } + /* + * 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 */ @@ -111882,6 +122049,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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]; @@ -111946,6 +122114,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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)) @@ -111959,6 +122128,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti 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; } } @@ -112005,10 +122175,12 @@ FLAC__bool process_subframe_( 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; @@ -112018,6 +122190,7 @@ FLAC__bool process_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 @@ -112026,6 +122199,7 @@ FLAC__bool process_subframe_( 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++) { @@ -112044,6 +122218,7 @@ FLAC__bool process_subframe_( } 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; @@ -112097,6 +122272,7 @@ FLAC__bool process_subframe_( } #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; @@ -112107,6 +122283,7 @@ FLAC__bool process_subframe_( 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) { @@ -112142,6 +122319,7 @@ FLAC__bool process_subframe_( } 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); @@ -112190,6 +122368,7 @@ FLAC__bool process_subframe_( } } + /* 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]); @@ -112383,6 +122562,7 @@ unsigned evaluate_lpc_subframe_( 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); @@ -112536,13 +122716,23 @@ unsigned find_best_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; @@ -112581,14 +122771,19 @@ void precompute_partition_info_sums_( 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; @@ -112613,6 +122808,7 @@ void precompute_partition_info_sums_( } } + /* now merge partitions for lower orders */ { unsigned from_partition = 0, to_partition = partitions; int partition_order; @@ -112642,6 +122838,7 @@ void precompute_partition_info_escapes_( 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; @@ -112658,17 +122855,20 @@ void precompute_partition_info_escapes_( 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; @@ -112714,6 +122914,12 @@ static FLaC__INLINE unsigned count_rice_bits_in_partition_( : (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 @@ -112814,6 +123020,14 @@ FLAC__bool set_partitioned_rice_( 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) { @@ -112942,6 +123156,10 @@ FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *d } 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; } @@ -112990,6 +123208,7 @@ FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder 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++) @@ -113069,11 +123288,22 @@ FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *e 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; @@ -113082,11 +123312,19 @@ FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *e 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); @@ -113146,6 +123384,9 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ 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); @@ -113318,6 +123559,7 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit 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) { @@ -113438,6 +123680,7 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit 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)) @@ -113486,7 +123729,7 @@ FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsign 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, -subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 + /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 )) return false; break; @@ -113532,7 +123775,7 @@ FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned r 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, -subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 + /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 )) return false; break; @@ -113669,6 +123912,7 @@ FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 #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 @@ -113709,6 +123953,7 @@ void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) 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; @@ -113823,7 +124068,9 @@ void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__rea 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)); @@ -114384,6 +124631,8 @@ extern "C" { #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 @@ -114412,6 +124661,7 @@ extern "C" { 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; @@ -114439,6 +124689,7 @@ extern "C" { #elif defined(__BEOS__) + /* Be */ # include typedef int16_t ogg_int16_t; typedef u_int16_t ogg_uint16_t; @@ -114448,6 +124699,7 @@ extern "C" { #elif defined (__EMX__) + /* OS/2 GCC */ typedef short ogg_int16_t; typedef unsigned short ogg_uint16_t; typedef int ogg_int32_t; @@ -114456,6 +124708,7 @@ extern "C" { #elif defined (DJGPP) + /* DJGPP */ typedef short ogg_int16_t; typedef int ogg_int32_t; typedef unsigned int ogg_uint32_t; @@ -114463,6 +124716,7 @@ extern "C" { #elif defined(R5900) + /* PS2 EE */ typedef long ogg_int64_t; typedef int ogg_int32_t; typedef unsigned ogg_uint32_t; @@ -114470,6 +124724,7 @@ extern "C" { #elif defined(__SYMBIAN32__) + /* Symbian GCC */ typedef signed short ogg_int16_t; typedef unsigned short ogg_uint16_t; typedef signed int ogg_int32_t; @@ -114508,6 +124763,8 @@ typedef struct { 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; @@ -114523,6 +124780,9 @@ ogg_uint32_t ogg_bitreverse(ogg_uint32_t x){ 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 */ @@ -114556,6 +124816,9 @@ typedef struct { } 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; @@ -114582,6 +124845,8 @@ typedef struct { 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); @@ -114618,10 +124883,14 @@ 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); @@ -114635,6 +124904,8 @@ 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); @@ -114667,6 +124938,21 @@ typedef struct vorbis_info{ 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; @@ -114675,6 +124961,9 @@ typedef struct vorbis_info{ 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; @@ -114705,6 +124994,7 @@ typedef struct vorbis_dsp_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; @@ -114719,12 +125009,15 @@ typedef struct vorbis_block{ 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; @@ -114734,12 +125027,27 @@ typedef struct vorbis_block{ } 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; @@ -114747,6 +125055,20 @@ typedef struct vorbis_comment{ } 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); @@ -114764,6 +125086,8 @@ 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, @@ -114780,6 +125104,7 @@ 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); @@ -114796,6 +125121,8 @@ 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 @@ -114853,6 +125180,7 @@ 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 @@ -114871,6 +125199,7 @@ struct ovectl_ratemanage_arg { double bitrate_av_window_center; }; + /* new rate setup */ #define OV_ECTL_RATEMANAGE2_GET 0x14 #define OV_ECTL_RATEMANAGE2_SET 0x15 @@ -114912,6 +125241,16 @@ extern "C" #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); @@ -114932,6 +125271,8 @@ typedef struct OggVorbis_File { 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; @@ -114942,6 +125283,7 @@ typedef struct OggVorbis_File { vorbis_info *vi; vorbis_comment *vc; + /* Decoding working state local storage */ ogg_int64_t pcm_offset; int ready_state; long current_serialno; @@ -115016,6 +125358,9 @@ extern int ov_halfrate_p(OggVorbis_File *vf); /*** 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 @@ -115074,6 +125419,7 @@ void oggpackB_writetrunc(oggpack_buffer *b,long bits){ *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); @@ -115107,6 +125453,7 @@ void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ 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); @@ -115166,9 +125513,11 @@ static void oggpack_writecopy_helper(oggpack_buffer *b, 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); @@ -115226,6 +125575,7 @@ 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]; @@ -115233,6 +125583,7 @@ long oggpack_look(oggpack_buffer *b,int bits){ bits+=b->endbit; if(b->endbyte+4>=b->storage){ + /* not the main path */ if(b->endbyte*8+bits>b->storage*8)return(-1); } @@ -115251,6 +125602,7 @@ long oggpack_look(oggpack_buffer *b,int bits){ 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; @@ -115258,6 +125610,7 @@ long oggpackB_look(oggpack_buffer *b,int bits){ bits+=b->endbit; if(b->endbyte+4>=b->storage){ + /* not the main path */ if(b->endbyte*8+bits>b->storage*8)return(-1); } @@ -115309,6 +125662,7 @@ 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]; @@ -115316,6 +125670,7 @@ long oggpack_read(oggpack_buffer *b,int 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; } @@ -115343,6 +125698,7 @@ long oggpack_read(oggpack_buffer *b,int bits){ return(ret); } +/* bits <= 32 */ long oggpackB_read(oggpack_buffer *b,int bits){ long ret; long m=32-bits; @@ -115350,6 +125706,7 @@ long oggpackB_read(oggpack_buffer *b,int 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; } @@ -115380,6 +125737,7 @@ long oggpack_read1(oggpack_buffer *b){ long ret; if(b->endbyte>=b->storage){ + /* not the main path */ ret=-1L; goto overflow; } @@ -115401,6 +125759,7 @@ long oggpackB_read1(oggpack_buffer *b){ long ret; if(b->endbyte>=b->storage){ + /* not the main path */ ret=-1L; goto overflow; } @@ -115442,6 +125801,9 @@ 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 @@ -115598,6 +125960,8 @@ int main(void){ static int six[7]={17,177,170,242,169,19,148}; static int sixB[7]={136,141,85,79,149,200,41}; + /* Test read/write together */ + /* Later we test against pregenerated bitstreams */ oggpack_writeinit(&o); fprintf(stderr,"\nSmall preclipped packing (LSb): "); @@ -115680,6 +126044,10 @@ int main(void){ oggpack_writeclear(&o); fprintf(stderr,"ok.\n"); + /********** lazy, cut-n-paste retest with MSb packing ***********/ + + /* Test read/write together */ + /* Later we test against pregenerated bitstreams */ oggpackB_writeinit(&o); fprintf(stderr,"\nSmall preclipped packing (MSb): "); @@ -115788,6 +126156,8 @@ int main(void){ #include #include +/* A complete description of Ogg framing exists in docs/framing.html */ + int ogg_page_version(ogg_page *og){ return((int)(og->header[4])); } @@ -115831,6 +126201,23 @@ long ogg_page_pageno(ogg_page *og){ (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;ibody_data)_ogg_free(os->body_data); @@ -115959,6 +126351,9 @@ int ogg_stream_destroy(ogg_stream_state *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); @@ -115974,11 +126369,16 @@ static void _os_lacing_expand(ogg_stream_state *os,int needed){ } } +/* 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; @@ -115996,10 +126396,14 @@ void ogg_page_checksum_set(ogg_page *og){ } } +/* 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) @@ -116008,12 +126412,19 @@ int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ 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; @@ -116021,10 +126432,12 @@ int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ 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; @@ -116032,6 +126445,20 @@ int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ 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; @@ -116042,6 +126469,11 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ 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;valsheader,"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++){ @@ -116082,6 +126521,8 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ } } + /* 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 @@ -116095,30 +126536,42 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ } } + /* 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 */ @@ -116129,6 +126582,7 @@ int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ return(ogg_stream_flush(os,og)); } + /* not enough data to construct a page and not end of stream */ return(0); } @@ -116136,6 +126590,22 @@ 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)); @@ -116143,6 +126613,7 @@ int ogg_sync_init(ogg_sync_state *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); @@ -116161,6 +126632,7 @@ int ogg_sync_destroy(ogg_sync_state *oy){ 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) @@ -116169,6 +126641,7 @@ char *ogg_sync_buffer(ogg_sync_state *oy, long size){ } 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) @@ -116178,6 +126651,7 @@ char *ogg_sync_buffer(ogg_sync_state *oy, long size){ oy->storage=newsize; } + /* expose a segment at least as large as requested at the fill mark */ return((char *)oy->data+oy->fill); } @@ -116187,6 +126661,16 @@ int ogg_sync_wrote(ogg_sync_state *oy, long 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; @@ -116196,11 +126680,14 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ 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; @@ -116208,26 +126695,35 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ 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; @@ -116251,6 +126747,7 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ 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; @@ -116259,25 +126756,48 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ 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; @@ -116293,10 +126813,12 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *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) @@ -116305,6 +126827,7 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ } 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)); @@ -116317,24 +126840,30 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ } } + /* 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){ @@ -116377,6 +126906,7 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ 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; } @@ -116394,6 +126924,7 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ 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; @@ -116430,11 +126961,17 @@ int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ 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); @@ -116444,6 +126981,7 @@ static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ 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; @@ -116508,6 +127046,8 @@ void checkpacket(ogg_packet *op,int len, int no, int pos){ exit(1); } + /* packet number just follows sequence/gap; adjust the input number + for that */ if(no==0){ sequence=0; }else{ @@ -116522,6 +127062,7 @@ void checkpacket(ogg_packet *op,int len, int no, int pos){ 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", @@ -116532,6 +127073,7 @@ void checkpacket(ogg_packet *op,int len, int no, int pos){ 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", @@ -116539,6 +127081,7 @@ void check_page(unsigned char *data,const int *header,ogg_page *og){ 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); @@ -116600,6 +127143,7 @@ void error(void){ 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, @@ -116607,6 +127151,7 @@ const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, 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, @@ -116621,6 +127166,7 @@ const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, 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, @@ -116635,6 +127181,7 @@ const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, 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, @@ -116650,6 +127197,7 @@ const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, 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, @@ -116672,6 +127220,7 @@ const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, 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, @@ -116724,6 +127273,7 @@ const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, 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, @@ -116754,6 +127304,7 @@ const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, 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, @@ -116799,6 +127350,7 @@ void test_pack(const int *pl, const int **headers, int byteskip, for(packets=0;;packets++)if(pl[packets]==-1)break; for(i=0;i0){ 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); @@ -116895,6 +127459,7 @@ void test_pack(const int *pl, const int **headers, int byteskip, bosflag=1; depacket+=op_de.bytes; + /* check eos flag */ if(eosflag){ fprintf(stderr,"Multiple decoded packets with eos flag!\n"); exit(1); @@ -116902,6 +127467,7 @@ void test_pack(const int *pl, const int **headers, int byteskip, if(op_de.e_o_s)eosflag=1; + /* check granulepos flag */ if(op_de.granulepos!=-1){ fprintf(stderr," granule:%ld ",(long)op_de.granulepos); } @@ -116945,7 +127511,11 @@ int main(void){ 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}; @@ -116954,6 +127524,7 @@ int main(void){ } { + /* 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}; @@ -116962,6 +127533,7 @@ int main(void){ } { + /* 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}; @@ -116970,6 +127542,7 @@ int main(void){ } { + /* large initial packet */ const int packets[]={4345,259,255,-1}; const int *headret[]={head1_3,head2_3,NULL}; @@ -116978,6 +127551,7 @@ int main(void){ } { + /* continuing packet test */ const int packets[]={0,4345,259,255,-1}; const int *headret[]={head1_4,head2_4,head3_4,NULL}; @@ -116985,6 +127559,7 @@ int main(void){ 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, @@ -117026,6 +127601,7 @@ int main(void){ } { + /* 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}; @@ -117034,6 +127610,8 @@ int main(void){ } { + /* 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}; @@ -117042,6 +127620,7 @@ int main(void){ } { + /* term only page. why not? */ const int packets[]={0,100,4080,-1}; const int *headret[]={head1_7,head2_7,head3_7,NULL}; @@ -117050,6 +127629,7 @@ int main(void){ } { + /* 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; @@ -117072,6 +127652,7 @@ int main(void){ _ogg_free(data); + /* retrieve finished pages */ for(i=0;i<5;i++){ if(ogg_stream_pageout(&os_en,&og[i])==0){ fprintf(stderr,"Too few pages output building sync tests!\n"); @@ -117080,6 +127661,7 @@ int main(void){ copy_page(&og[i]); } + /* Test lost pages on pagein/packetout: no rollback */ { ogg_page temp; ogg_packet test; @@ -117101,9 +127683,12 @@ int main(void){ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); + /* skip */ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); + /* do we get the expected results/packets? */ + if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); @@ -117121,6 +127706,7 @@ int main(void){ fprintf(stderr,"ok.\n"); } + /* Test lost pages on pagein/packetout: rollback with continuation */ { ogg_page temp; ogg_packet test; @@ -117144,9 +127730,12 @@ int main(void){ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); ogg_sync_pageout(&oy,&temp); + /* skip */ ogg_sync_pageout(&oy,&temp); ogg_stream_pagein(&os_de,&temp); + /* do we get the expected results/packets? */ + if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); @@ -117164,8 +127753,10 @@ int main(void){ fprintf(stderr,"ok.\n"); } + /* the rest only test sync */ { ogg_page og_de; + /* Test fractional page inputs: incomplete capture */ fprintf(stderr,"Testing sync on partial inputs... "); ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, @@ -117173,16 +127764,20 @@ int main(void){ ogg_sync_wrote(&oy,3); if(ogg_sync_pageout(&oy,&og_de)>0)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); @@ -117200,6 +127795,7 @@ int main(void){ fprintf(stderr,"ok.\n"); } + /* Test fractional page inputs: page + incomplete capture */ { ogg_page og_de; fprintf(stderr,"Testing sync on 1+partial inputs... "); @@ -117230,11 +127826,13 @@ int main(void){ 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); @@ -117265,6 +127863,7 @@ int main(void){ fprintf(stderr,"ok.\n"); } + /* Test recapture: page + garbage + page */ { ogg_page og_de; fprintf(stderr,"Testing recapture... "); @@ -117306,6 +127905,7 @@ int main(void){ fprintf(stderr,"ok.\n"); } + /* Free page data that was previously copied */ { for(i=0;i<5;i++){ free_page(&og[i]); @@ -117354,6 +127954,7 @@ int main(void){ #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 @@ -117463,15 +128064,30 @@ extern int _ve_envelope_mark(vorbis_dsp_state *v); #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 */ @@ -117481,6 +128097,7 @@ typedef struct static_codebook{ 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; @@ -117488,7 +128105,10 @@ typedef struct static_codebook{ 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; @@ -117498,6 +128118,7 @@ typedef struct encode_aux_nearestmatch{ 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; @@ -117525,6 +128146,9 @@ typedef struct codebook{ 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 */ @@ -117598,6 +128222,7 @@ typedef void vorbis_look_floor; typedef void vorbis_look_residue; typedef void vorbis_look_transform; +/* mode ************************************************************/ typedef struct { int blockflag; int windowtype; @@ -117635,9 +128260,15 @@ extern void drft_clear(drft_lookup *l); /*** 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 *); @@ -117680,6 +128311,7 @@ typedef struct{ int mult; /* 1 2 3 or 4 */ int postlist[VIF_POSIT+2]; /* first two implicit */ + /* encode side analysis parameters */ float maxover; float maxunder; float maxerr; @@ -117691,6 +128323,7 @@ typedef struct{ } vorbis_info_floor1; +/* Residue backend generic *****************************************/ typedef struct{ void (*pack) (vorbis_info_residue *,oggpack_buffer *); vorbis_info_residue *(*unpack)(vorbis_info *,oggpack_buffer *); @@ -117708,9 +128341,11 @@ typedef struct{ } vorbis_func_residue; typedef struct vorbis_info_residue0{ +/* block-partitioned VQ coded straight residue */ long begin; long end; + /* first stage (lossless partitioning) */ int grouping; /* group n vectors per partition */ int partitions; /* possible codebooks for a partition */ int groupbook; /* huffbook for partitioning */ @@ -117722,6 +128357,7 @@ typedef struct vorbis_info_residue0{ } vorbis_info_residue0; +/* Mapping backend generic *****************************************/ typedef struct{ void (*pack) (vorbis_info *,vorbis_info_mapping *, oggpack_buffer *); @@ -117751,6 +128387,7 @@ typedef struct vorbis_info_mapping0{ #define EHMER_MAX 56 #endif +/* psychoacoustic setup ********************************************/ #define P_BANDS 17 /* 62Hz to 16kHz */ #define P_LEVELS 8 /* 30dB to 100dB */ #define P_LEVEL_0 30. /* 30 dB */ @@ -117791,6 +128428,7 @@ typedef struct vorbis_info_psy{ typedef struct{ int eighth_octave_lines; + /* for block long/short tuning; encode only */ float preecho_thresh[VE_BANDS]; float postecho_thresh[VE_BANDS]; float stretch_penalty; @@ -117798,6 +128436,7 @@ typedef struct{ float ampmax_att_per_sec; + /* channel coupling config */ int coupling_pkHz[PACKETBLOBS]; int coupling_pointlimit[2][PACKETBLOBS]; int coupling_prepointamp[PACKETBLOBS]; @@ -117914,6 +128553,22 @@ extern void hf_reduction(vorbis_info_psy_global *g, /*** Start of inlined file: os.h ***/ #ifndef _OS_H #define _OS_H +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the XIPHOPHORUS Company http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os.h,v 1.1 2007/06/07 17:49:18 jules_rms Exp $ + + ********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" @@ -118010,6 +128665,12 @@ void *_alloca(size_t size); #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; @@ -118028,6 +128689,7 @@ 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 */ @@ -118067,6 +128729,7 @@ 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) {} @@ -118075,6 +128738,7 @@ static int vorbis_ftoi(double f){ #endif /* _OS_H */ /*** End of inlined file: os.h ***/ +/* encode side bitrate tracking */ typedef struct bitrate_manager_state { int managed; @@ -118131,6 +128795,7 @@ static int ilog2(unsigned int v){ } typedef struct private_state { + /* local lookup storage */ envelope_lookup *ve; /* envelope lookup */ int window[2]; vorbis_look_transform **transform[2]; /* block, type */ @@ -118142,6 +128807,10 @@ typedef struct private_state { 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; @@ -118151,6 +128820,12 @@ typedef struct private_state { 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 { @@ -118196,8 +128871,16 @@ typedef struct highlevel_encode_setup { 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; @@ -118257,6 +128940,7 @@ extern vorbis_func_mapping *_mapping_P[]; #include +/* 20log10(x) */ #define VORBIS_IEEE_FLOAT32 1 #ifdef VORBIS_IEEE_FLOAT32 @@ -118270,6 +128954,7 @@ static float unitnorm(float x){ 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; @@ -118296,11 +128981,23 @@ static float unitnorm(float x){ #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)) @@ -118309,6 +129006,7 @@ static float unitnorm(float x){ 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; @@ -118318,14 +129016,21 @@ int vorbis_analysis(vorbis_block *vb, ogg_packet *op){ 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); @@ -118338,11 +129043,13 @@ int vorbis_analysis(vorbis_block *vb, ogg_packet *op){ return(0); } +/* there was no great place to put this.... */ void _analysis_output_always(const 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"); @@ -118370,6 +129077,7 @@ void _analysis_output_always(const char *base,int i,float *v,int n,int bark,int } } fclose(of); + /* } */ } void _analysis_output(char *base,int i,float *v,int n,int bark,int dB, @@ -118398,6 +129106,7 @@ void _analysis_output(char *base,int i,float *v,int n,int bark,int dB, #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; @@ -118417,6 +129126,8 @@ void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ 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; @@ -118440,6 +129151,7 @@ int vorbis_bitrate_managed(vorbis_block *vb){ 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; @@ -118456,6 +129168,8 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ 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 */ @@ -118465,11 +129179,23 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ 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){ @@ -118491,7 +129217,9 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ 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++; @@ -118501,7 +129229,9 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ } } + /* 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--; @@ -118511,7 +129241,12 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ } } + /* 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; @@ -118527,12 +129262,15 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ 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){ @@ -118540,6 +129278,7 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ }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); @@ -118558,6 +129297,7 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ } } + /* 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; @@ -118629,6 +129369,7 @@ extern void _vorbis_apply_window(float *d,int *winno,long *blocksizes, #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, @@ -118637,6 +129378,45 @@ extern void vorbis_lpc_predict(float *coeff,float *prime,int m, #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 @@ -118669,6 +129449,7 @@ int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ 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; @@ -118676,6 +129457,7 @@ void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ link->ptr=vb->localstore; vb->reap=link; } + /* highly conservative */ vb->localalloc=bytes; vb->localstore=_ogg_malloc(vb->localalloc); vb->localtop=0; @@ -118687,7 +129469,9 @@ void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ } } +/* 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; @@ -118696,12 +129480,14 @@ void _vorbis_block_ripcord(vorbis_block *vb){ _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; } @@ -118724,6 +129510,10 @@ int vorbis_block_clear(vorbis_block *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; @@ -118742,19 +129532,24 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ 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++) @@ -118772,16 +129567,20 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ 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)); @@ -118791,13 +129590,17 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ 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)); @@ -118812,6 +129615,7 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ 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; @@ -118819,11 +129623,14 @@ int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ 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); @@ -118888,6 +129695,7 @@ void vorbis_dsp_clear(vorbis_dsp_state *v){ } 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); @@ -118903,10 +129711,14 @@ float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ 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; @@ -118931,11 +129743,14 @@ static void _preextrapolate_helper(vorbis_dsp_state *v){ 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, @@ -118948,6 +129763,8 @@ static void _preextrapolate_helper(vorbis_dsp_state *v){ } } +/* 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; @@ -118957,24 +129774,37 @@ int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ 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])); @@ -118987,6 +129817,9 @@ int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ 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); @@ -118994,6 +129827,8 @@ int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ 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; @@ -119003,10 +129838,19 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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){ @@ -119026,6 +129870,7 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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; @@ -119046,15 +129894,19 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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,".");*/ } } @@ -119064,6 +129916,10 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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; @@ -119076,8 +129932,17 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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; @@ -119086,6 +129951,7 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ } } + /* advance storage vectors and clean up */ { int new_centerNext=ci->blocksizes[1]/2; int movementW=centerNext-new_centerNext; @@ -119106,6 +129972,7 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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{ @@ -119117,6 +129984,7 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ } } + /* done */ return(1); } @@ -119150,6 +130018,10 @@ int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ 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; @@ -119194,15 +130066,22 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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]; @@ -119211,6 +130090,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ } }else{ if(v->W){ + /* 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; @@ -119219,6 +130099,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ for(;iwindow[0]-hs); float *pcm=v->pcm[j]+prevCenter; float *p=vb->pcm[j]; @@ -119227,6 +130108,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ } } + /* the copy section */ { float *pcm=v->pcm[j]+thisCenter; float *p=vb->pcm[j]+n; @@ -119240,6 +130122,10 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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; @@ -119252,6 +130138,17 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ } + /* 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{ @@ -119263,11 +130160,20 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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; @@ -119285,6 +130191,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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 } */ @@ -119294,11 +130201,14 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ } } + /* 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; @@ -119320,6 +130230,11 @@ int vorbis_synthesis_read(vorbis_dsp_state *v,int 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; @@ -119332,7 +130247,19 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ 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;icenterW=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; @@ -119358,6 +130287,7 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ 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; @@ -119411,19 +130341,28 @@ float *vorbis_window(vorbis_dsp_state *v,int W){ #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 */ @@ -119442,8 +130381,13 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ 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; @@ -119464,16 +130408,23 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ } } + /* 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); @@ -119483,43 +130434,56 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ 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)){ @@ -119530,6 +130494,7 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ 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; @@ -119539,6 +130504,7 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ break; case 1: + /* ordered */ { long length=oggpack_read(opb,5)+1; s->lengthlist=(long*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); @@ -119553,13 +130519,18 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ } 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); @@ -119577,6 +130548,7 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ break; } + /* quantized values */ s->quantlist=(long*)_ogg_malloc(sizeof(*s->quantlist)*quantvals); for(i=0;iquantlist[i]=oggpack_read(opb,s->q_quant); @@ -119588,6 +130560,7 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ goto _errout; } + /* all set */ return(0); _errout: @@ -119596,11 +130569,27 @@ int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *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); @@ -119609,6 +130598,7 @@ int vorbis_book_errorv(codebook *book,float *a){ 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;kdec_maxlength; long lo,hi; @@ -119641,6 +130639,7 @@ STIN long decode_packed_entry_number(codebook *book, oggpack_buffer *b){ lok = oggpack_look(b, --read); if(lok<0)return -1; + /* bisect search for the codeword in the ordered list */ { ogg_uint32_t testword=ogg_bitreverse((ogg_uint32_t)lok); @@ -119661,14 +130660,31 @@ STIN long decode_packed_entry_number(codebook *book, oggpack_buffer *b){ 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); @@ -119766,6 +130782,10 @@ long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch, } #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 @@ -119853,6 +130873,7 @@ int main(){ 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 */ @@ -119867,6 +130888,7 @@ int main(){ fprintf(stderr,"OK.\n"); fprintf(stderr,"\tunpacking/decoding %ld... ",ptr); + /* transfer the write data to a read buffer and unpack/read */ oggpack_readinit(&read,oggpack_get_buffer(&write),oggpack_bytes(&write)); if(vorbis_staticbook_unpack(&read,&s)){ fprintf(stderr,"Error unpacking codebook.\n"); @@ -119893,6 +130915,8 @@ int main(){ ptr++; } + /* The above is the trivial stuff; now try unquantizing a log scale codebook */ + exit(0); } @@ -119940,6 +130964,7 @@ void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){ 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; @@ -119974,6 +130999,9 @@ void _ve_envelope_clear(envelope_lookup *e){ 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, @@ -119985,22 +131013,38 @@ static int _ve_amp(envelope_lookup *ve, 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; @@ -120017,6 +131061,9 @@ static int _ve_amp(envelope_lookup *ve, 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=VE_AMP)filters[j].ampptr=0; } + /* look at min/max, decide trigger */ if(valmax>gi->preecho_thresh[j]+penalty){ ret|=1; ret|=4; @@ -120086,6 +131140,7 @@ long _ve_envelope_search(vorbis_dsp_state *v){ 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)); @@ -120283,6 +131338,8 @@ typedef struct { long frames; } vorbis_look_floor0; +/***********************************************/ + static void floor0_free_info(vorbis_info_floor *i){ vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; if(info){ @@ -120335,6 +131392,14 @@ static vorbis_info_floor *floor0_unpack (vorbis_info *vi,oggpack_buffer *opb){ 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){ @@ -120346,8 +131411,17 @@ static void floor0_map_lazy_init(vorbis_block *vb, 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) @@ -120389,6 +131463,9 @@ static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ 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) @@ -120417,6 +131494,7 @@ static int floor0_inverse2(vorbis_block *vb,vorbis_look_floor *i, 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], @@ -120428,6 +131506,7 @@ static int floor0_inverse2(vorbis_block *vb,vorbis_look_floor *i, return(0); } +/* export hooks */ vorbis_func_floor floor0_exportbundle={ NULL,&floor0_unpack,&floor0_look,&floor0_free_info, &floor0_free_look,&floor0_inverse1,&floor0_inverse2 @@ -120488,6 +131567,8 @@ typedef struct lsfit_acc{ long an; } lsfit_acc; +/***********************************************/ + static void floor1_free_info(vorbis_info_floor *i){ vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; if(info){ @@ -120499,6 +131580,10 @@ static void floor1_free_info(vorbis_info_floor *i){ 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); @@ -120513,12 +131598,14 @@ static void floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ 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 */ @@ -120527,6 +131614,7 @@ static void floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ 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); @@ -120543,12 +131631,14 @@ static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ 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 */ @@ -120564,6 +131654,7 @@ static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ } } + /* read the post list */ info->mult=oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ rangebits=oggpack_read(opb,4); @@ -120600,17 +131691,29 @@ static vorbis_look_floor *floor1_look(vorbis_dsp_state *vd, look->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; @@ -120626,6 +131729,8 @@ static vorbis_look_floor *floor1_look(vorbis_dsp_state *vd, break; } + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ for(i=0;itwofitweight/(na+1); @@ -120876,6 +131983,7 @@ static void fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ } if(an){ + /* need 64 bit multiplies, which C doesn't give portably as int */ double fx=x; double fy=y; double fx2=x2; @@ -120886,6 +131994,7 @@ static void fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ *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; @@ -120897,6 +132006,16 @@ static void fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ } } +/*static void fit_line_point(lsfit_acc *a,int fits,int *y0,int *y1){ + long y=0; + int i; + + for(i=0;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); @@ -121021,6 +132150,7 @@ int *floor1_fit(vorbis_block *vb,void *look_, } 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; @@ -121028,6 +132158,7 @@ int *floor1_fit(vorbis_block *vb,void *look_, 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; @@ -121036,6 +132167,7 @@ int *floor1_fit(vorbis_block *vb,void *look_, 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; @@ -121062,6 +132194,9 @@ int *floor1_fit(vorbis_block *vb,void *look_, output[0]=post_Y(fit_valueA,fit_valueB,0); output[1]=post_Y(fit_valueA,fit_valueB,1); + /* fill in posts marked as not using a fit; we will zero + back out to 'unused' when encoding them so long as curve + interpolation doesn't force them into use */ for(i=2;iloneighbor[i-2]; int hn=look->hineighbor[i-2]; @@ -121120,6 +132255,7 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, codebook *books=ci->fullbooks; static long seq=0; + /* quantize values to multiplier spec */ if(post){ for(i=0;iloneighbor[i-2]; int hn=look->hineighbor[i-2]; @@ -121163,6 +132300,12 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, 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; @@ -121180,13 +132323,17 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, } } + /* 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]; @@ -121197,6 +132344,7 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, int cshift=0; int k,l; + /* generate the partition's first stage cascade value */ if(csubbits){ int maxval[8]; for(k=0;kphrasebits+= vorbis_book_encode(books+info->class_book[classx],cval,opb); @@ -121234,12 +132383,16 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, #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 { @@ -121258,6 +132411,8 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, } { + /* 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; @@ -121295,12 +132450,14 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ 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]; @@ -121308,6 +132465,7 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ int csub=1<class_book[classx],&vb->opb); @@ -121327,6 +132485,7 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ 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]], @@ -121379,6 +132538,7 @@ static int floor1_inverse2(vorbis_block *vb,vorbis_look_floor *in,void *memo, int j; if(memo){ + /* render the lines */ int *fit_value=(int *)memo; int hx=0; int lx=0; @@ -121404,6 +132564,7 @@ static int floor1_inverse2(vorbis_block *vb,vorbis_look_floor *in,void *memo, return(0); } +/* export hooks */ vorbis_func_floor floor1_exportbundle={ &floor1_pack,&floor1_unpack,&floor1_look,&floor1_free_info, &floor1_free_look,&floor1_inverse1,&floor1_inverse2 @@ -121426,6 +132587,9 @@ vorbis_func_floor floor1_exportbundle={ #if JUCE_USE_OGGVORBIS +/* general handling of the header and the vorbis_info structure (and + substructures) */ + #include #include #include @@ -121467,6 +132631,8 @@ void vorbis_comment_add_tag(vorbis_comment *vc, const char *tag, char *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){ @@ -121489,6 +132655,7 @@ char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){ 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++; @@ -121524,11 +132691,14 @@ void vorbis_comment_clear(vorbis_comment *vc){ 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)); @@ -121554,6 +132724,7 @@ void vorbis_info_clear(vorbis_info *vi){ 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) @@ -121571,6 +132742,8 @@ void vorbis_info_clear(vorbis_info *vi){ 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); @@ -121627,17 +132800,22 @@ static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ 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;ifloors=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; @@ -121654,7 +132835,10 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *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; @@ -121662,7 +132846,10 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *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; @@ -121670,7 +132857,9 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *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); @@ -121691,26 +132880,36 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ 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); } @@ -121718,6 +132917,7 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) case 0x03: /* least significant *bit* is read first */ if(vi->rate==0){ + /* um... we didn't get the initial header */ return(OV_EBADHEADER); } @@ -121725,12 +132925,14 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) 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; } @@ -121739,13 +132941,17 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) 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); @@ -121765,12 +132971,16 @@ 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; @@ -121796,13 +133006,16 @@ static int _vorbis_pack_books(oggpack_buffer *opb,vorbis_info *vi){ 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); @@ -121812,18 +133025,21 @@ static int _vorbis_pack_books(oggpack_buffer *opb,vorbis_info *vi){ 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); @@ -121873,9 +133089,12 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v, 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)); @@ -121886,6 +133105,8 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v, op->granulepos=0; op->packetno=0; + /* second header packet (comments) **********************************/ + oggpack_reset(&opb); if(_vorbis_pack_comment(&opb,vc))goto err_out; @@ -121899,6 +133120,8 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v, 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; @@ -121940,6 +133163,34 @@ double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ /*** 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 @@ -121956,12 +133207,19 @@ double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ #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 */ @@ -121969,6 +133227,8 @@ float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ aut[j]=d; } + /* Generate lpc coefficients from autocorr values */ + error=aut[0]; for(i=0;i +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ float vorbis_fromdBlook(float a){ int i=vorbis_ftoi(a*((float)(-(1<>(INVSQ_LOOKUP_I_SHIFT-1); long d=(a&INVSQ_LOOKUP_I_MASK)<<(16-INVSQ_LOOKUP_I_SHIFT); /* 0.16 */ @@ -122323,6 +133626,8 @@ long vorbis_invsqlook_i(long a,long e){ 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: @@ -122330,6 +133635,8 @@ float vorbis_fromdBlook_i(long a){ FROMdB_LOOKUP[i>>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; @@ -122346,6 +133653,7 @@ long vorbis_coslook_i(long a){ 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; @@ -122373,10 +133681,13 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, }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); } @@ -122610,6 +133921,7 @@ static long COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ #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); @@ -122617,17 +133929,20 @@ float vorbis_coslook(float a){ 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<>(INVSQ_LOOKUP_I_SHIFT-1); long d=(a&INVSQ_LOOKUP_I_MASK)<<(16-INVSQ_LOOKUP_I_SHIFT); /* 0.16 */ @@ -122652,6 +133971,8 @@ long vorbis_invsqlook_i(long a,long e){ 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: @@ -122659,6 +133980,8 @@ float vorbis_fromdBlook_i(long a){ FROMdB_LOOKUP[i>>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; @@ -122691,9 +134014,13 @@ static int MLOOP_2[64]={ 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); @@ -122723,7 +134050,11 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, 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; @@ -122744,6 +134075,10 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, 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; @@ -122759,6 +134094,10 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, } + /* 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 @@ -122768,6 +134107,7 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, amp=vorbis_fromdBlook_i(ampi* /* n.4 */ vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ ampoffseti); /* 8.12[0] */ curve[i]*=amp; @@ -122777,6 +134117,11 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, #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; @@ -122794,10 +134139,13 @@ void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, p *= w-lsp[j]; } if(j==m){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ q*=w-lsp[j-1]; p*=p*(4.f-w*w); q*=q; }else{ + /* even order filter; still symmetric */ p*=p*(2.f-w); q*=q*(2.f+w); } @@ -122828,6 +134176,13 @@ 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; @@ -122838,15 +134193,18 @@ static int Laguerre_With_Deflation(float *a,int ord,float *r){ 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 */ @@ -122870,6 +134228,8 @@ static int Laguerre_With_Deflation(float *a,int ord,float *r){ r[m-1]=newx; + /* forward deflation */ + for(i=m;i>0;i--) defl[i-1]+=newx*defl[i]; defl++; @@ -122878,6 +134238,7 @@ static int Laguerre_With_Deflation(float *a,int ord,float *r){ 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; @@ -122908,10 +134269,14 @@ static int Newton_Raphson(float *a,int ord,float *r){ 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; @@ -122921,9 +134286,15 @@ int vorbis_lpc_to_lsp(float *lpc,float *lsp,int m){ 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; @@ -122936,9 +134307,11 @@ int vorbis_lpc_to_lsp(float *lpc,float *lsp,int m){ 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); @@ -122979,6 +134352,15 @@ int vorbis_lpc_to_lsp(float *lpc,float *lsp,int m){ #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){ @@ -123002,6 +134384,13 @@ static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, 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); @@ -123021,6 +134410,7 @@ static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, 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); @@ -123032,6 +134422,7 @@ static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, } } +/* 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)); @@ -123221,6 +134612,7 @@ static int mapping0_forward(vorbis_block *vb){ _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 @@ -123231,8 +134623,11 @@ static int mapping0_forward(vorbis_block *vb){ _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 @@ -123289,9 +134684,12 @@ static int mapping0_forward(vorbis_block *vb){ 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]; @@ -123330,6 +134728,12 @@ static int mapping0_forward(vorbis_block *vb){ } #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 @@ -123343,6 +134747,10 @@ static int mapping0_forward(vorbis_block *vb){ } #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, @@ -123358,6 +134766,11 @@ static int mapping0_forward(vorbis_block *vb){ } #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]; @@ -123390,6 +134803,9 @@ static int mapping0_forward(vorbis_block *vb){ } #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]= @@ -123397,7 +134813,10 @@ static int mapping0_forward(vorbis_block *vb){ 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, @@ -123421,6 +134840,7 @@ static int mapping0_forward(vorbis_block *vb){ logmdct, logmask); + /* lower rate by way of higher noise curve */ _vp_offset_and_mix(psy_look, noise, tone, @@ -123442,6 +134862,8 @@ static int mapping0_forward(vorbis_block *vb){ logmdct, logmask); + /* we also interpolate a range of intermediate curves for + intermediate rates */ for(k=1;kflr[info->floorsubmap[submap]], @@ -123459,6 +134881,20 @@ static int mapping0_forward(vorbis_block *vb){ } 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); @@ -123499,13 +134935,18 @@ static int mapping0_forward(vorbis_block *vb){ 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]; @@ -123547,6 +134988,12 @@ static int mapping0_forward(vorbis_block *vb){ #endif } + /* our iteration is now based on masking curve, not prequant and + coupling. Only one prequant/coupling step */ + + /* quantize/couple */ + /* incomplete implementation that assumes the tree is all depth + one, or no tree at all */ if(info->coupling_steps){ _vp_couple(k, &ci->psy_g_param, @@ -123560,6 +135007,7 @@ static int mapping0_forward(vorbis_block *vb){ 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; @@ -123582,6 +135030,7 @@ static int mapping0_forward(vorbis_block *vb){ couple_bundle,NULL,zerobundle,ch_in_bundle,classifications); } + /* ok, done encoding. Next protopacket. */ } } @@ -123609,6 +135058,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ 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]]]-> @@ -123620,6 +135070,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ 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]]){ @@ -123628,6 +135079,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ } } + /* recover the residue into our working vectors */ for(i=0;isubmaps;i++){ int ch_in_bundle=0; for(j=0;jchannels;j++){ @@ -123645,6 +135097,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ 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]]; @@ -123672,6 +135125,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ } } + /* compute and apply spectral envelope */ for(i=0;ichannels;i++){ float *pcm=vb->pcm[i]; int submap=info->chmuxlist[i]; @@ -123680,14 +135134,18 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ 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, @@ -123701,6 +135159,12 @@ vorbis_func_mapping mapping0_exportbundle={ /*** 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 @@ -123718,6 +135182,9 @@ vorbis_func_mapping mapping0_exportbundle={ #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)); @@ -123729,6 +135196,8 @@ void mdct_init(mdct_lookup *lookup,int n){ lookup->trig=T; lookup->bitrev=bitrev; +/* trig lookups... */ + for(i=0;iscale=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]; @@ -123778,6 +135250,7 @@ STIN void mdct_butterfly_8(DATA_TYPE *x){ } +/* 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]; @@ -123812,6 +135285,7 @@ STIN void mdct_butterfly_16(DATA_TYPE *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]; @@ -123875,6 +135349,7 @@ STIN void mdct_butterfly_32(DATA_TYPE *x){ } +/* N point first stage butterfly (in place, 2 register) */ STIN void mdct_butterfly_first(DATA_TYPE *T, DATA_TYPE *x, int points){ @@ -123921,6 +135396,7 @@ STIN void mdct_butterfly_first(DATA_TYPE *T, }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, @@ -124059,6 +135535,8 @@ void mdct_backward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ 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; @@ -124090,6 +135568,8 @@ void mdct_backward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ mdct_butterflies(init,out+n2,n2); mdct_bitreverse(init,out); + /* roatate + window */ + { DATA_TYPE *oX1=out+n2+n4; DATA_TYPE *oX2=out+n2+n4; @@ -124154,6 +135634,10 @@ void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ 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; @@ -124199,6 +135683,8 @@ void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ mdct_butterflies(init,w+n2,n2); mdct_bitreverse(init,w); + /* roatate + window */ + T=init->trig+n2; x0=out+n2; @@ -124237,25 +135723,38 @@ void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ #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[]={ - -51, -52, -53, -54, -55, -56, -57, -58, - -59, -60, -61, -62, -63, -64, -65, -66, - -67, -68, -69, -70, -71, -72, -73, -74, - -75, -76, -77, -78, -80, -81, -82, -83, - -84, -85, -86, -87, -88, -88, -89, -89, - -90, -91, -91, -92, -93, -94, -95, -96, - -96, -97, -98, -98, -99, -99,-100,-100, - -101,-102,-103,-104,-106,-107,-107,-107, - -107,-105,-103,-102,-101, -99, -98, -96, - -95, -95, -96, -97, -96, -95, -93, -90, - -80, -70, -50, -40, -30, -30, -30, -30 + /*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, @@ -124298,6 +135797,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124340,6 +135840,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124382,6 +135883,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124424,6 +135926,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124466,6 +135969,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124508,6 +136012,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124550,6 +136055,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124592,6 +136098,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124634,6 +136141,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124676,6 +136184,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124718,6 +136227,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124760,6 +136270,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124802,6 +136313,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124844,6 +136356,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124886,6 +136399,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -124928,6 +136442,7 @@ static float tonemasks[P_BANDS][6][EHMER_MAX]={ -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, @@ -125042,7 +136557,12 @@ static float ***setup_tone_curves(float curveatt_dB[P_BANDS],float binHz,int n, memset(workc,0,sizeof(workc)); for(i=0;i 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); @@ -125098,6 +136643,9 @@ static float ***setup_tone_curves(float curveatt_dB[P_BANDS],float binHz,int n, for(j=0;j-200.f)break; ret[i][m][0]=j; @@ -125194,11 +136744,14 @@ void _vp_psy_init(vorbis_look_psy *p,vorbis_info_psy *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;itonecurves=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)); @@ -125285,6 +136839,7 @@ void _vp_psy_clear(vorbis_look_psy *p){ } } +/* octave/(8*eighth_octave_lines) x scale and dB y scale */ static void seed_curve(float *seed, const float **curves, float amp, @@ -125322,6 +136877,8 @@ static void seed_loop(vorbis_look_psy *p, long n=p->n,i; float dBoffset=vi->max_curve_dB-specmax; + /* prime the working vector with peak values */ + for(i=0;ioctave[i]; @@ -125368,6 +136925,7 @@ static void seed_chase(float *seeds, int linesper, long n){ if(i1 && ampstack[stack-1]<=ampstack[stack-2] && iampstack[i]){ @@ -125394,8 +136955,12 @@ static void seed_chase(float *seeds, int linesper, long n){ seeds[pos]=ampstack[i]; } + /* there. Linear time. I now remember this was on a problem set I + had in Grad Skool... I didn't solve it at the time ;-) */ + } +/* bleaugh, this is more complicated than it needs to be */ #include static void max_seeds(vorbis_look_psy *p, float *seed, @@ -125729,11 +137294,14 @@ void _vp_tonemask(vorbis_look_psy *p, 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); @@ -125757,18 +137325,38 @@ void _vp_offset_and_mix(vorbis_look_psy *p, 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; @@ -125833,6 +137421,12 @@ static void precomputed_couple_point(float premag, *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); @@ -125854,6 +137448,7 @@ static float round_hypot(float a, float 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, @@ -125877,6 +137472,7 @@ float **_vp_quantize_couple_memo(vorbis_block *vb, return(ret); } +/* this is for per-channel noise normalization */ static int apsort(const void *a, const void *b){ float f1=fabs(**(float**)a); float f2=fabs(**(float**)b); @@ -125985,8 +137581,20 @@ void _vp_couple(int blobno, int i,j,k,n=p->n; + /* 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]]){ @@ -126005,6 +137613,7 @@ void _vp_couple(int blobno, 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]]; @@ -126046,6 +137655,13 @@ void _vp_couple(int blobno, } } +/* 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, @@ -126055,6 +137671,7 @@ void hf_reduction(vorbis_info_psy_global *g, int limit=g->coupling_pointlimit[p->vi->blockflag][PACKETBLOBS/2]; for(i=0; icoupling_steps; i++){ + /* for(j=start; 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"); @@ -126183,15 +137810,45 @@ void res0_free_look(vorbis_look_residue *i){ 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); @@ -126224,8 +137881,12 @@ void res0_pack(vorbis_info_residue *vr,oggpack_buffer *opb){ 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); @@ -126238,6 +137899,7 @@ void res0_pack(vorbis_info_residue *vr,oggpack_buffer *opb){ } +/* 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)); @@ -126327,11 +137989,13 @@ vorbis_look_residue *res0_look(vorbis_dsp_state *vd, 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; @@ -126351,6 +138015,7 @@ static int local_book_besterror(codebook *book,float *a){ best=(best*tt->quantvals)+tt->quantmap[i]; } + /* regular lattices are easy :-) */ if(book->c->lengthlist[best]<=0){ const static_codebook *c=book->c; @@ -126408,6 +138073,7 @@ static long **_01class(vorbis_block *vb,vorbis_look_residue *vl, 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; @@ -126416,6 +138082,10 @@ static long **_01class(vorbis_block *vb,vorbis_look_residue *vl, 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;iinfo; + /* move all this setup out later */ int samples_per_partition=info->grouping; int possible_partitions=info->partitions; int n=info->end-info->begin; @@ -126525,6 +138199,7 @@ static int _01forward(oggpack_buffer *opb, 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; @@ -126545,10 +138220,16 @@ static int _01forward(oggpack_buffer *opb, 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*/ @@ -126568,6 +138250,7 @@ static int _01forward(oggpack_buffer *opb, } } + /* now we encode interleaved residual values for the partitions */ for(k=0;kbegin; @@ -126605,9 +138288,22 @@ static int _01forward(oggpack_buffer *opb, } } + /*{ + 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; @@ -126630,8 +138327,11 @@ static int _01inverse(vorbis_block *vb,vorbis_look_residue *vl, for(s=0;sstages;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; @@ -126640,6 +138340,7 @@ static int _01inverse(vorbis_block *vb,vorbis_look_residue *vl, } } + /* now we decode residual values for the partitions */ for(k=0;kbegin+i*samples_per_partition; @@ -126660,13 +138361,17 @@ static int _01inverse(vorbis_block *vb,vorbis_look_residue *vl, } #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,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; @@ -126830,12 +138545,14 @@ int res2_inverse(vorbis_block *vb,vorbis_look_residue *vl, 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]; @@ -126909,6 +138626,7 @@ vorbis_func_residue residue2_exportbundle={ #include #include +/**** pack/unpack helpers ******************************************/ int _ilog(unsigned int v){ int ret=0; while(v){ @@ -126918,10 +138636,15 @@ int _ilog(unsigned int v){ 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; @@ -126945,6 +138668,9 @@ float _float32_unpack(long val){ 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]; @@ -126956,16 +138682,26 @@ ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ if(length>0){ 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 @@ -126977,6 +138713,9 @@ ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ } } + /* 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]; @@ -126987,6 +138726,8 @@ ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ if(sparsecount==0)count++; } + /* bitreverse the words because our bitwise packer/unpacker is LSb + endian */ for(i=0,count=0;ientries,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; @@ -127027,6 +138776,11 @@ long _book_maptype1_quantvals(const static_codebook *b){ } } +/* 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){ @@ -127035,8 +138789,17 @@ float *_book_unquantize(const static_codebook *b,int n,int *sparsemap){ 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){ @@ -127114,6 +138877,8 @@ void vorbis_staticbook_destroy(static_codebook *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); @@ -127142,11 +138907,13 @@ static int sort32a(const void *a,const void *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++; @@ -127155,7 +138922,18 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ 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); @@ -127170,6 +138948,7 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ sortindex=(int*)alloca(n*sizeof(*sortindex)); c->codelist=(ogg_uint32_t*)_ogg_malloc(n*sizeof(*c->codelist)); + /* the index is a reverse index */ for(i=0;idec_firsttablen); long lo=0,hi=0; @@ -127220,6 +139001,9 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ 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; @@ -127258,9 +139042,13 @@ int _best(codebook *book, float *a, int step){ #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; @@ -127279,6 +139067,7 @@ int _best(codebook *book, float *a, int step){ 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*/ @@ -127286,12 +139075,14 @@ int _best(codebook *book, float *a, int step){ } #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; @@ -127311,7 +139102,10 @@ int _best(codebook *book, float *a, int step){ } } + /* 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); @@ -127326,6 +139120,7 @@ int _best(codebook *book, float *a, int step){ } if(nt){ + /* optimized using the decision tree */ while(1){ float c=0.f; float *p=book->valuelist+nt->p[ptr]; @@ -127344,6 +139139,7 @@ int _best(codebook *book, float *a, int step){ } #endif + /* brute force it! */ { const static_codebook *c=book->c; int i,besti=-1; @@ -127360,6 +139156,20 @@ int _best(codebook *book, float *a, int step){ e+=dim; } + /*if(savebest!=-1 && savebest!=besti){ + fprintf(stderr,"brute force/pigeonhole disagreement:\n" + "original:"); + for(i=0;ivaluelist+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); } } @@ -127380,11 +139190,26 @@ long vorbis_book_codelen(codebook *book,int entry){ #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, @@ -127395,6 +139220,7 @@ static_codebook test1={ }; static float *test1_result=NULL; +/* linear, full mapping, nonsequential */ static_codebook test2={ 4,3, NULL, @@ -127405,6 +139231,7 @@ static_codebook test2={ }; 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, @@ -127415,6 +139242,7 @@ static_codebook test3={ }; 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, @@ -127433,6 +139261,7 @@ static float test4_result[]={-3,-3,-3, 4,-3,-3, -1,-3,-3, -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, @@ -127478,6 +139307,7 @@ void run_test(static_codebook *b,float *comp){ } 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... "); @@ -127500,6 +139330,19 @@ int main(){ /*** 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 @@ -128679,6 +140522,20 @@ static void drftb1(int n, float *c, float *ch, float *wa, int *ifac){ goto L115; L109: +/* The radix five case can be translated later..... */ +/* if(ip!=5)goto L112; + + ix2=iw+ido; + ix3=ix2+ido; + ix4=ix3+ido; + if(na!=0) + dradb5(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1); + else + dradb5(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1); + na=1-na; + goto L115; + + L112:*/ if(na!=0) dradbg(ido,ip,l1,idl1,ch,ch,ch,c,c,wa+iw-1); else @@ -128747,13 +140604,17 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ 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); @@ -128761,6 +140622,8 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ 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); @@ -128769,21 +140632,26 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ 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; @@ -128792,13 +140660,17 @@ int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ 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); @@ -128813,10 +140685,12 @@ int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ 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; @@ -128830,7 +140704,9 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ 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); } @@ -128842,6 +140718,7 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ v>>=1; } + /* read our mode and pre/post windowsize */ mode=oggpack_read(&opb,modebits); } if(mode==-1)return(OV_EBADPACKET); @@ -128849,8 +140726,10 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ } 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; @@ -128882,6 +140761,10 @@ int vorbis_synthesis_halfrate_p(vorbis_info *vi){ #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; @@ -128909,6 +140792,13 @@ 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; @@ -128983,6 +140873,7 @@ typedef struct { 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} @@ -130959,6 +142850,7 @@ static static_codebook **_floor_books[10]={ }; static vorbis_info_floor1 _floor[10]={ + /* 128 x 4 */ { 1,{0},{4},{2},{0}, {{1,2,3,4}}, @@ -130966,6 +142858,7 @@ static vorbis_info_floor1 _floor[10]={ 60,30,500, 1.,18., -1 }, + /* 256 x 4 */ { 1,{0},{4},{2},{0}, {{1,2,3,4}}, @@ -130973,6 +142866,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -130980,6 +142874,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -130987,6 +142882,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -130995,6 +142891,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -131002,6 +142899,7 @@ static vorbis_info_floor1 _floor[10]={ 60,30,500, 1,18., -1 }, + /* 256 x 4 (low bitrate version) */ { 1,{0},{4},{2},{0}, {{1,2,3,4}}, @@ -131009,6 +142907,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -131017,6 +142916,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -131025,6 +142925,7 @@ static vorbis_info_floor1 _floor[10]={ 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}}, @@ -152013,8 +163914,11 @@ static static_codebook _huff_book__44cn1_sm_short = { }; /*** 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}, @@ -152023,6 +163927,7 @@ static vorbis_info_residue0 _residue_44_low={ 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}, @@ -152031,6 +163936,7 @@ static vorbis_info_residue0 _residue_44_mid={ 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}, @@ -152283,6 +164189,8 @@ static vorbis_mapping_template _mapres_template_44_stereo[]={ /*** Start of inlined file: psych_44.h ***/ +/* preecho trigger settings *****************************************/ + static vorbis_info_psy_global _psy_global_44[5]={ {8, /* lines per eighth octave */ @@ -152317,7 +164225,9 @@ static vorbis_info_psy_global _psy_global_44[5]={ }, }; +/* 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 */ @@ -152325,6 +164235,7 @@ static compandblock _psy_compand_44[6]={ 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 */ @@ -152332,6 +164243,7 @@ static compandblock _psy_compand_44[6]={ 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 */ @@ -152339,6 +164251,7 @@ static compandblock _psy_compand_44[6]={ 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 */ @@ -152346,6 +164259,7 @@ static compandblock _psy_compand_44[6]={ 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 */ @@ -152353,6 +164267,7 @@ static compandblock _psy_compand_44[6]={ 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 */ @@ -152362,216 +164277,367 @@ static compandblock _psy_compand_44[6]={ }} }; +/* 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 */ -/* 4 */ +/* {{-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}}}, @@ -152595,15 +164661,23 @@ static int _psy_noise_suppress[12]={ }; 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, }; @@ -152611,66 +164685,112 @@ 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 */ -/* 1 */ + /* {{ 25, 14, 4}, 0, 0}, *//* 1 */ {{ 25, 12, 2}, 0, 0}, /* 1 */ -/* 2 */ + /* {{ 20, 10, -2}, 0, 0}, *//* 2 */ {{ 20, 9, -3}, 0, 0}, /* 2 */ {{ 20, 9, -4}, 0, 0}, /* 3 */ {{ 20, 9, -4}, 0, 0}, /* 4 */ @@ -152682,14 +164802,20 @@ static att3 _psy_tone_masteratt_44[12]={ {{ 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 }; @@ -152701,6 +164827,7 @@ static int _noise_part_long_44[11]={ }; 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., }; @@ -152733,6 +164860,7 @@ static double _psy_compand_long_mapping[12]={ }; 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. }; @@ -164301,6 +176429,8 @@ static static_codebook _huff_book__44un1__short = { }; /*** End of inlined file: res_books_uncoupled.h ***/ +/***** residue backends *********************************************/ + static vorbis_info_residue0 _residue_44_low_un={ 0,-1, -1, 8,-1, {0}, @@ -164311,6 +176441,7 @@ static vorbis_info_residue0 _residue_44_low_un={ 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}, @@ -164319,12 +176450,16 @@ static vorbis_info_residue0 _residue_44_mid_un={ 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}} @@ -164779,12 +176914,15 @@ static att3 _psy_tone_masteratt_8[3]={ }; 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}}}, @@ -164798,7 +176936,9 @@ static noise3 _psy_noisebias_8[3]={ {-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}, @@ -164854,6 +176994,8 @@ static int _psy_ath_abs_8[3]={ /*** 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}, @@ -165071,12 +177213,15 @@ static att3 _psy_tone_masteratt_11[3]={ }; 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}}}, @@ -165220,7 +177365,9 @@ ve_setup_data_template ve_setup_11_uncoupled={ /*** 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}, @@ -165249,6 +177396,8 @@ static att3 _psy_tone_masteratt_16[4]={ }; 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 */ @@ -165256,6 +177405,7 @@ static vp_adjblock _vp_tonemask_adj_16[4]={ }; 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}}}, @@ -165274,6 +177424,7 @@ static noise3 _psy_noisebias_16_short[4]={ }; 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}}}, @@ -165292,6 +177443,7 @@ static noise3 _psy_noisebias_16_impulse[4]={ }; 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}}}, @@ -165325,6 +177477,8 @@ static int _psy_ath_abs_16[4]={ /*** Start of inlined file: residue_16.h ***/ +/***** residue backends *********************************************/ + static static_bookblock _resbook_16s_0={ { {0}, @@ -165973,8 +178127,10 @@ static void vorbis_encode_floor_setup(vorbis_info *vi,double s,int block, 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; @@ -165994,6 +178150,7 @@ static void vorbis_encode_floor_setup(vorbis_info *vi,double s,int block, 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++; @@ -166019,6 +178176,7 @@ static void vorbis_encode_global_psych_setup(vorbis_info *vi,double s, 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; @@ -166041,6 +178199,7 @@ static void vorbis_encode_global_stereo(vorbis_info *vi, 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]; @@ -166115,6 +178274,8 @@ static void vorbis_encode_tonemask_setup(vorbis_info *vi,double s,int block, 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; @@ -166143,6 +178304,7 @@ static void vorbis_encode_compand_setup(vorbis_info *vi,double s,int block, ds=1.; } + /* interpolate the compander settings */ for(i=0;inoisecompand[i]=in[is].data[i]*(1.-ds)+in[is+1].data[i]*ds; return; @@ -166179,6 +178341,8 @@ static void vorbis_encode_noisebias_setup(vorbis_info *vi,double s,int block, for(i=0;inoiseoff[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;iresidue_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; @@ -166291,15 +178457,22 @@ static void vorbis_encode_residue_setup(vorbis_info *vi, } } + /* 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.; @@ -166308,6 +178481,10 @@ static void vorbis_encode_residue_setup(vorbis_info *vi, 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; @@ -166317,6 +178494,7 @@ static void vorbis_encode_residue_setup(vorbis_info *vi, } } +/* we assume two maps in this encoder */ static void vorbis_encode_map_n_res_setup(vorbis_info *vi,double s, vorbis_mapping_template *maps){ @@ -166379,10 +178557,13 @@ static void get_setup_template(vorbis_info *vi, 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; @@ -166402,6 +178583,13 @@ static void get_setup_template(vorbis_info *vi, 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; @@ -166411,21 +178599,32 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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, @@ -166436,11 +178635,13 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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], @@ -166464,6 +178665,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 3); } + /* tone masking setup */ vorbis_encode_tonemask_setup(vi,hi->block[i0].tone_mask_setting,0, setup->psy_tone_masteratt, setup->psy_tone_0dB, @@ -166483,6 +178685,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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); @@ -166498,6 +178701,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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, @@ -166509,6 +178713,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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, @@ -166538,6 +178743,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ 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{ @@ -166686,6 +178892,7 @@ int vorbis_encode_setup_managed(vorbis_info *vi, return ret; } + /* initialize management with sane defaults */ hi->managed=1; hi->bitrate_min=min_bitrate; hi->bitrate_max=max_bitrate; @@ -166731,6 +178938,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ switch(number){ + /* now deprecated *****************/ case OV_ECTL_RATEMANAGE_GET: { @@ -166749,6 +178957,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ } return(0); + /* now deprecated *****************/ case OV_ECTL_RATEMANAGE_SET: { struct ovectl_ratemanage_arg *ai= @@ -166763,6 +178972,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ } return 0; + /* now deprecated *****************/ case OV_ECTL_RATEMANAGE_AVG: { struct ovectl_ratemanage_arg *ai= @@ -166774,6 +178984,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ } } return(0); + /* now deprecated *****************/ case OV_ECTL_RATEMANAGE_HARD: { struct ovectl_ratemanage_arg *ai= @@ -166792,6 +179003,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ } return(0); + /* replacement ratemanage interface */ case OV_ECTL_RATEMANAGE2_GET: { struct ovectl_ratemanage2_arg *ai= @@ -166814,6 +179026,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *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) @@ -166912,6 +179125,37 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ #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){ @@ -166926,16 +179170,31 @@ static long _get_data(OggVorbis_File *vf){ 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; @@ -166946,9 +179205,11 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, 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); @@ -166956,6 +179217,8 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, 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); @@ -166965,6 +179228,11 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, } } +/* 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; @@ -166987,14 +179255,20 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ } } + /* 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, @@ -167006,6 +179280,8 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, ogg_page og; ogg_int64_t ret; + /* the below guards against garbage seperating the last and + first pages of two links. */ while(searchedos.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); @@ -167097,6 +179378,15 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, 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; @@ -167109,11 +179399,14 @@ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ 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; @@ -167122,6 +179415,8 @@ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ } } + /* fetch beginning PCM offset */ + if(vf->dataoffsets[i]!=-1){ ogg_int64_t accumulated=0; long lastblock=-1; @@ -167134,11 +179429,14 @@ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ 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 */ @@ -167150,16 +179448,21 @@ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ } 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); @@ -167167,6 +179470,7 @@ static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ 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; @@ -167203,40 +179507,66 @@ static int _open_seekable2(OggVorbis_File *vf){ 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; @@ -167247,6 +179577,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, 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 @@ -167256,8 +179587,11 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, 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); @@ -167265,10 +179599,24 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, 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 @@ -167299,8 +179647,11 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, 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) @@ -167316,6 +179667,18 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, } } + /* 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; @@ -167323,6 +179686,9 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, if(vf->seekable){ 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 @@ -167336,6 +179702,8 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, 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); @@ -167353,6 +179721,8 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, } } +/* 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); @@ -167367,21 +179737,30 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, 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); @@ -167406,6 +179785,7 @@ static int _ov_open2(OggVorbis_File *vf){ return 0; } +/* clear out the OggVorbis_File struct */ int ov_clear(OggVorbis_File *vf){ if(vf){ vorbis_block_clear(&vf->vb); @@ -167435,6 +179815,14 @@ int ov_clear(OggVorbis_File *vf){ 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); @@ -167453,6 +179841,9 @@ int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ 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; @@ -167478,6 +179869,11 @@ int ov_halfrate_p(OggVorbis_File *vf){ 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) { @@ -167500,14 +179896,25 @@ int ov_test_open(OggVorbis_File *vf){ 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); @@ -167518,12 +179925,18 @@ long ov_bitrate(OggVorbis_File *vf,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{ @@ -167540,6 +179953,10 @@ long ov_bitrate(OggVorbis_File *vf,int i){ } } +/* 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; @@ -167551,6 +179968,7 @@ long ov_bitrate_instant(OggVorbis_File *vf){ 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)); @@ -167561,6 +179979,11 @@ long ov_serialnumber(OggVorbis_File *vf,int 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); @@ -167575,6 +179998,11 @@ ogg_int64_t ov_raw_total(OggVorbis_File *vf,int 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); @@ -167589,6 +180017,11 @@ ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ } } +/* 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); @@ -167603,6 +180036,13 @@ double ov_time_total(OggVorbis_File *vf,int i){ } } +/* 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; @@ -167612,6 +180052,10 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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 */ @@ -167619,6 +180063,21 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ _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; @@ -167634,6 +180093,7 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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){ @@ -167674,10 +180134,12 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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 */ @@ -167713,12 +180175,19 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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; @@ -167729,11 +180198,19 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ 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]; @@ -167749,6 +180226,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ if(end-beginpcm_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; @@ -167822,9 +180304,14 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ 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); @@ -167853,6 +180340,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } } + /* verify result */ if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ result=OV_EFAULT; goto seek_error; @@ -167862,17 +180350,24 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ 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; @@ -167889,14 +180384,19 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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++) @@ -167908,6 +180408,7 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ }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); @@ -167933,6 +180434,8 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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); @@ -167948,7 +180451,10 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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); @@ -167958,19 +180464,24 @@ int ov_time_seek(OggVorbis_File *vf,double seconds){ if(!vf->seekable)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); @@ -167980,28 +180491,34 @@ int ov_time_seek_page(OggVorbis_File *vf,double seconds){ if(!vf->seekable)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; @@ -168012,6 +180529,7 @@ double ov_time_tell(OggVorbis_File *vf){ 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); @@ -168022,6 +180540,14 @@ double ov_time_tell(OggVorbis_File *vf){ 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) @@ -168039,6 +180565,7 @@ vorbis_info *ov_info(OggVorbis_File *vf,int link){ } } +/* grr, strong typing, grr, no templates/inheritence, grr */ vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ if(vf->seekable){ if(link<0) @@ -168063,6 +180590,37 @@ static int host_is_big_endian() { 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; @@ -168079,6 +180637,7 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length, if(samples)break; } + /* suck in another packet */ { int ret=_fetch_and_process_packet(vf,NULL,1,1); if(ret==OV_EOF) @@ -168091,6 +180650,8 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length, 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; @@ -168100,6 +180661,7 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length, if(samples <= 0) return OV_EINVAL; + /* a tight loop to pack each size */ { int val; if(word==1){ @@ -168191,6 +180753,18 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length, } } +/* 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){ @@ -168211,6 +180785,7 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length, } } + /* suck in another packet */ { int ret=_fetch_and_process_packet(vf,NULL,1,1); if(ret==OV_EOF)return(0); @@ -168237,6 +180812,7 @@ static void _ov_splice(float **pcm,float **lappcm, 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); @@ -168268,12 +180847,16 @@ static int _ov_initset(OggVorbis_File *vf){ 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); @@ -168282,11 +180865,15 @@ static int _ov_initprime(OggVorbis_File *vf){ 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(lapcountvd,&pcm); if(samples==0){ for(i=0;ichannels;i++) @@ -168315,6 +180906,8 @@ static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, } } +/* 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; @@ -168326,6 +180919,10 @@ int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){ if(vf1->ready_stateready_statevd,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); } @@ -168383,20 +180985,25 @@ static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos, lappcm[i]=(float*) alloca(sizeof(**lappcm)*n1); _ov_getlap(vf,vi,&vf->vd,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); } @@ -168439,20 +181046,25 @@ static int _ov_d_seek_lap(OggVorbis_File *vf,double pos, lappcm[i]=(float*) alloca(sizeof(**lappcm)*n1); _ov_getlap(vf,vi,&vf->vd,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); } @@ -171048,9 +183660,18 @@ namespace jpeglibNamespace #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) @@ -171066,6 +183687,8 @@ namespace jpeglibNamespace #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 @@ -171075,6 +183698,7 @@ namespace jpeglibNamespace #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 @@ -171107,11 +183731,46 @@ typedef unsigned char boolean; #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 @@ -171135,6 +183794,9 @@ typedef char JSAMPLE; #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)) @@ -171144,8 +183806,20 @@ typedef short JSAMPLE; #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; @@ -171162,6 +183836,15 @@ typedef char JOCTET; #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 */ @@ -171172,41 +183855,84 @@ 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 @@ -171217,22 +183943,53 @@ typedef int boolean; #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)*/ @@ -171244,11 +184001,34 @@ typedef int boolean; #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__ @@ -171258,10 +184038,23 @@ typedef int boolean; #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 @@ -171275,8 +184068,17 @@ typedef int boolean; /* 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 */ @@ -171284,11 +184086,23 @@ typedef int boolean; #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 */ @@ -171300,33 +184114,89 @@ 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 */ @@ -171334,11 +184204,18 @@ typedef struct { 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 */ @@ -171346,6 +184223,8 @@ typedef struct { 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 { @@ -171354,8 +184233,11 @@ struct jpeg_marker_struct { 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 */ @@ -171365,6 +184247,8 @@ typedef enum { 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 */ @@ -171378,12 +184262,16 @@ typedef enum { #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 */\ @@ -171392,19 +184280,35 @@ typedef enum { 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 */ @@ -171412,17 +184316,28 @@ struct jpeg_compress_struct { 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 */ @@ -171430,6 +184345,10 @@ struct jpeg_compress_struct { 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 */ @@ -171438,36 +184357,74 @@ struct jpeg_compress_struct { 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; @@ -171481,16 +184438,27 @@ struct jpeg_compress_struct { 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 */ @@ -171505,40 +184473,99 @@ struct jpeg_decompress_struct { 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 */ @@ -171549,7 +184576,11 @@ struct jpeg_decompress_struct { 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 */ @@ -171560,30 +184591,63 @@ struct jpeg_decompress_struct { 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; @@ -171597,14 +184661,31 @@ struct jpeg_decompress_struct { 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 { @@ -171612,17 +184693,40 @@ struct jpeg_error_mgr { 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)); @@ -171632,6 +184736,8 @@ struct jpeg_progress_mgr { 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 */ @@ -171641,6 +184747,8 @@ struct jpeg_destination_mgr { 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 */ @@ -171652,6 +184760,17 @@ struct jpeg_source_mgr { 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 @@ -171660,6 +184779,7 @@ 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, @@ -171696,19 +184816,40 @@ struct jpeg_memory_mgr { 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 @@ -171760,9 +184901,17 @@ typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); #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)) @@ -171773,13 +184922,18 @@ 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)); @@ -171799,6 +184953,7 @@ EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, 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, @@ -171806,36 +184961,50 @@ EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, 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)); @@ -171843,41 +185012,66 @@ 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; }; @@ -171905,16 +185099,27 @@ 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 */ @@ -171931,15 +185136,20 @@ typedef enum { /* Operating modes for buffer controllers */ #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, @@ -171947,6 +185157,7 @@ struct jpeg_c_main_controller { 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, @@ -171958,12 +185169,14 @@ struct jpeg_c_prep_controller { 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, @@ -171971,6 +185184,7 @@ struct jpeg_color_converter { 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, @@ -171981,8 +185195,10 @@ struct jpeg_downsampler { 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, @@ -171990,40 +185206,51 @@ struct jpeg_forward_dct { 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, @@ -172031,15 +185258,18 @@ struct jpeg_d_main_controller { 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, @@ -172051,25 +185281,38 @@ struct jpeg_d_post_controller { 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, @@ -172077,9 +185320,11 @@ typedef JMETHOD(void, inverse_DCT_method_ptr, 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, @@ -172093,6 +185338,7 @@ struct jpeg_upsampler { 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, @@ -172100,6 +185346,7 @@ struct jpeg_color_deconverter { 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, @@ -172109,11 +185356,23 @@ struct jpeg_color_quantizer { 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) \ @@ -172125,6 +185384,8 @@ struct jpeg_color_quantizer { #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 @@ -172161,6 +185422,7 @@ struct jpeg_color_quantizer { #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)); @@ -172176,6 +185438,7 @@ 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)); @@ -172193,8 +185456,10 @@ 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, @@ -172203,11 +185468,14 @@ EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, 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; }; @@ -172219,10 +185487,17 @@ struct jvirt_barray_control { long dummy; }; /* 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 */ @@ -172237,6 +185512,7 @@ typedef enum { 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") @@ -172389,11 +185665,16 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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))) @@ -172426,6 +185707,7 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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)) @@ -172439,6 +185721,7 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") (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))) @@ -172493,11 +185776,15 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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) @@ -172513,6 +185800,8 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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 @@ -172522,6 +185811,7 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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 @@ -172553,6 +185843,17 @@ typedef unsigned char boolean; /* 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 @@ -172567,6 +185868,16 @@ typedef unsigned char boolean; #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 @@ -172581,8 +185892,22 @@ typedef unsigned char boolean; #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) \ @@ -172655,6 +185980,11 @@ typedef enum { /* JPEG marker codes */ 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)) @@ -172679,11 +186009,17 @@ static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ /*** 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); @@ -172691,6 +186027,12 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) 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 */ @@ -172700,8 +186042,10 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) } 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; @@ -172719,21 +186063,43 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) 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) { @@ -172754,6 +186120,13 @@ jpeg_suppress_tables (j_compress_ptr cinfo, boolean 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) { @@ -172761,11 +186134,13 @@ jpeg_finish_compress (j_compress_ptr cinfo) 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++) { @@ -172774,16 +186149,28 @@ jpeg_finish_compress (j_compress_ptr cinfo) 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) @@ -172804,6 +186191,8 @@ jpeg_write_marker (j_compress_ptr cinfo, int marker, } } +/* Same, but piecemeal. */ + GLOBAL(void) jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen) { @@ -172822,17 +186211,54 @@ 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 ***/ @@ -172840,6 +186266,21 @@ jpeg_write_tables (j_compress_ptr cinfo) /*** 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) { @@ -172849,14 +186290,35 @@ jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) 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) @@ -172868,15 +186330,22 @@ jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, 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; @@ -172887,6 +186356,11 @@ jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, 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) @@ -172900,23 +186374,33 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE 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; } @@ -172926,6 +186410,11 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, /*** 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 @@ -172934,6 +186423,8 @@ jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, #endif #endif +/* Private buffer controller object */ + typedef struct { struct jpeg_c_coef_controller pub; /* public fields */ @@ -172942,13 +186433,24 @@ typedef struct { 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 @@ -172960,9 +186462,14 @@ METHODDEF(boolean) compress_output 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 { @@ -172976,6 +186483,10 @@ start_iMCU_row (j_compress_ptr cinfo) coef->MCU_vert_offset = 0; } +/* + * Initialize for a processing pass. + */ + METHODDEF(void) start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { @@ -173008,6 +186519,16 @@ start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) } } +/* + * 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) { @@ -173019,10 +186540,20 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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]; @@ -173038,6 +186569,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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++) { @@ -173045,6 +186577,7 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } } 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++) { @@ -173055,14 +186588,20 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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; @@ -173070,6 +186609,27 @@ compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) #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) { @@ -173084,21 +186644,28 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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, @@ -173106,6 +186673,7 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) (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]; @@ -173114,6 +186682,11 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } } + /* 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; @@ -173134,10 +186707,24 @@ compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } } + /* 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) { @@ -173149,6 +186736,10 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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) @@ -173157,10 +186748,12 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) (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]; @@ -173172,14 +186765,18 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } } + /* 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; @@ -173187,6 +186784,10 @@ compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) #endif /* FULL_COEF_BUFFER_SUPPORTED */ +/* + * Initialize coefficient buffer controller. + */ + GLOBAL(void) jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) { @@ -173198,8 +186799,11 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) 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; @@ -173217,6 +186821,7 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); #endif } else { + /* We only need a single-MCU buffer. */ JBLOCKROW buffer; int i; @@ -173235,19 +186840,58 @@ jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) /*** 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. */ @@ -173259,6 +186903,10 @@ typedef my_color_converter * my_cconvert_ptr; #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) { @@ -173266,6 +186914,7 @@ rgb_ycc_start (j_compress_ptr cinfo) 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))); @@ -173276,12 +186925,31 @@ rgb_ycc_start (j_compress_ptr cinfo) 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, @@ -173306,12 +186974,20 @@ rgb_ycc_convert (j_compress_ptr cinfo, 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); @@ -173319,6 +186995,15 @@ rgb_ycc_convert (j_compress_ptr cinfo, } } +/**************** 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, @@ -173341,6 +187026,7 @@ rgb_gray_convert (j_compress_ptr cinfo, 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); @@ -173348,6 +187034,14 @@ rgb_gray_convert (j_compress_ptr cinfo, } } +/* + * 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, @@ -173372,14 +187066,23 @@ cmyk_ycck_convert (j_compress_ptr cinfo, 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); @@ -173387,6 +187090,12 @@ cmyk_ycck_convert (j_compress_ptr cinfo, } } +/* + * 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, @@ -173409,6 +187118,12 @@ grayscale_convert (j_compress_ptr cinfo, } } +/* + * 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, @@ -173422,6 +187137,7 @@ null_convert (j_compress_ptr cinfo, 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]; @@ -173435,11 +187151,20 @@ null_convert (j_compress_ptr cinfo, } } +/* + * 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) { @@ -173449,8 +187174,10 @@ jinit_color_converter (j_compress_ptr cinfo) (*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) @@ -173481,6 +187208,7 @@ jinit_color_converter (j_compress_ptr cinfo) break; } + /* Check num_components, set conversion method based on requested space */ switch (cinfo->jpeg_color_space) { case JCS_GRAYSCALE: if (cinfo->num_components != 1) @@ -173555,6 +187283,19 @@ jinit_color_converter (j_compress_ptr cinfo) /*** 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__ @@ -173567,6 +187308,23 @@ typedef INT32 DCTELEM; /* must have 32 bits */ 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 */ @@ -173577,10 +187335,21 @@ typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ #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 @@ -173593,6 +187362,8 @@ typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ #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)); @@ -173616,13 +187387,42 @@ 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 @@ -173634,6 +187434,8 @@ EXTERN(void) jpeg_idct_1x1 #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 @@ -173647,14 +187449,22 @@ EXTERN(void) jpeg_idct_1x1 /* 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 @@ -173662,6 +187472,15 @@ typedef struct { 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) { @@ -173674,13 +187493,19 @@ start_pass_fdctmgr (j_compress_ptr cinfo) 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, @@ -173695,8 +187520,15 @@ start_pass_fdctmgr (j_compress_ptr cinfo) #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, @@ -173726,6 +187558,14 @@ start_pass_fdctmgr (j_compress_ptr cinfo) #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] = { @@ -173758,12 +187598,22 @@ start_pass_fdctmgr (j_compress_ptr cinfo) } } +/* + * 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]; @@ -173773,6 +187623,7 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -173799,8 +187650,10 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, } } + /* 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]; @@ -173808,6 +187661,18 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, 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 @@ -173835,7 +187700,9 @@ 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]; @@ -173845,6 +187712,7 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -173872,14 +187740,23 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, } } + /* 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); } } @@ -173888,6 +187765,10 @@ forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, #endif /* DCT_FLOAT_SUPPORTED */ +/* + * Initialize FDCT manager. + */ + GLOBAL(void) jinit_forward_dct (j_compress_ptr cinfo) { @@ -173924,6 +187805,7 @@ jinit_forward_dct (j_compress_ptr cinfo) break; } + /* Mark divisor tables unallocated */ for (i = 0; i < NUM_QUANT_TBLS; i++) { fdct->divisors[i] = NULL; #ifdef DCT_FLOAT_SUPPORTED @@ -173941,6 +187823,12 @@ jinit_forward_dct (j_compress_ptr cinfo) /*** 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_ @@ -173950,20 +187838,27 @@ jinit_forward_dct (j_compress_ptr cinfo) #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[])); @@ -173972,12 +187867,23 @@ EXTERN(void) jpeg_gen_optimal_table /* 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 @@ -173997,9 +187903,11 @@ typedef struct { 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]; @@ -174011,6 +187919,10 @@ typedef struct { 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 */ @@ -174018,6 +187930,7 @@ typedef struct { 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)); @@ -174027,6 +187940,12 @@ METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, 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) { @@ -174052,10 +187971,14 @@ start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) 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, @@ -174068,21 +187991,33 @@ start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) 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) @@ -174094,6 +188029,11 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, 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 = @@ -174101,12 +188041,15 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int 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]; @@ -174118,6 +188061,9 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, 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; @@ -174126,14 +188072,29 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, 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++) { @@ -174145,6 +188106,9 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, } } +/* 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) \ @@ -174153,23 +188117,36 @@ jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, 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); @@ -174208,6 +188185,8 @@ flush_bits (working_state * state) 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) @@ -174216,34 +188195,48 @@ encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, 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; @@ -174253,19 +188246,25 @@ encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, 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; @@ -174273,6 +188272,7 @@ encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, } } + /* 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; @@ -174280,6 +188280,10 @@ encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, return TRUE; } +/* + * Emit a restart marker & resynchronize predictions. + */ + LOCAL(boolean) emit_restart (working_state * state, int restart_num) { @@ -174291,12 +188295,19 @@ emit_restart (working_state * state, int restart_num) 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) { @@ -174305,17 +188316,20 @@ encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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]; @@ -174324,13 +188338,16 @@ encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -174343,27 +188360,47 @@ encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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[]) @@ -174372,50 +188409,70 @@ htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, 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) { @@ -174423,10 +188480,13 @@ encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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--; @@ -174444,6 +188504,34 @@ encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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[]) { @@ -174455,14 +188543,24 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) 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++) { @@ -174472,6 +188570,8 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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++) { @@ -174481,12 +188581,15 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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]; @@ -174495,6 +188598,7 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) 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]; @@ -174502,8 +188606,11 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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); @@ -174511,6 +188618,17 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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 */ @@ -174524,12 +188642,18 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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++) { @@ -174540,9 +188664,14 @@ jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) } } + /* 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) { @@ -174553,6 +188682,9 @@ finish_pass_gather (j_compress_ptr cinfo) 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)); @@ -174579,6 +188711,10 @@ finish_pass_gather (j_compress_ptr cinfo) #endif /* ENTROPY_OPT_SUPPORTED */ +/* + * Module initialization routine for Huffman entropy encoding. + */ + GLOBAL(void) jinit_huff_encoder (j_compress_ptr cinfo) { @@ -174591,6 +188727,7 @@ jinit_huff_encoder (j_compress_ptr cinfo) 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 @@ -174606,17 +188743,27 @@ jinit_huff_encoder (j_compress_ptr cinfo) /*** 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 { @@ -174630,14 +188777,20 @@ jinit_compress_master (j_compress_ptr cinfo) 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 ***/ @@ -174647,8 +188800,15 @@ jinit_compress_master (j_compress_ptr cinfo) /*** 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 */ @@ -174657,15 +188817,23 @@ typedef struct { 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)); @@ -174675,11 +188843,16 @@ METHODDEF(void) process_data_buffer_main 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; @@ -174711,6 +188884,12 @@ start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) } } +/* + * 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, @@ -174719,22 +188898,37 @@ process_data_simple_main (j_compress_ptr cinfo, 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; @@ -174746,6 +188940,11 @@ process_data_simple_main (j_compress_ptr cinfo, #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, @@ -174757,6 +188956,7 @@ process_data_buffer_main (j_compress_ptr cinfo, 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++) { @@ -174765,35 +188965,50 @@ process_data_buffer_main (j_compress_ptr cinfo, 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++; } @@ -174801,6 +189016,10 @@ process_data_buffer_main (j_compress_ptr cinfo, #endif /* FULL_MAIN_BUFFER_SUPPORTED */ +/* + * Initialize main buffer controller. + */ + GLOBAL(void) jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) { @@ -174814,11 +189033,17 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) 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) @@ -174835,6 +189060,7 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) #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) @@ -174850,6 +189076,8 @@ jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) /*** Start of inlined file: jcmarker.c ***/ #define JPEG_INTERNALS +/* Private state */ + typedef struct { struct jpeg_marker_writer pub; /* public fields */ @@ -174858,8 +189086,21 @@ typedef struct { 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; @@ -174872,6 +189113,7 @@ emit_byte (j_compress_ptr cinfo, int val) LOCAL(void) emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) +/* Emit a marker code */ { emit_byte(cinfo, 0xFF); emit_byte(cinfo, (int) mark); @@ -174879,13 +189121,20 @@ emit_marker (j_compress_ptr cinfo, JPEG_MARKER 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; @@ -174908,6 +189157,7 @@ emit_dqt (j_compress_ptr cinfo, int index) 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)); @@ -174922,6 +189172,7 @@ emit_dqt (j_compress_ptr cinfo, int index) LOCAL(void) emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) +/* Emit a DHT marker */ { JHUFF_TBL * htbl; int length, i; @@ -174958,6 +189209,9 @@ emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) 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]; @@ -174997,6 +189251,7 @@ emit_dac (j_compress_ptr cinfo) LOCAL(void) emit_dri (j_compress_ptr cinfo) +/* Emit a DRI marker */ { emit_marker(cinfo, M_DRI); @@ -175007,6 +189262,7 @@ emit_dri (j_compress_ptr cinfo) LOCAL(void) emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) +/* Emit a SOF marker */ { int ci; jpeg_component_info *compptr; @@ -175015,6 +189271,7 @@ emit_sof (j_compress_ptr cinfo, JPEG_MARKER 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); @@ -175035,6 +189292,7 @@ emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) LOCAL(void) emit_sos (j_compress_ptr cinfo) +/* Emit a SOS marker */ { int i, td, ta; jpeg_component_info *compptr; @@ -175051,6 +189309,11 @@ emit_sos (j_compress_ptr cinfo) 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) @@ -175069,7 +189332,19 @@ emit_sos (j_compress_ptr cinfo) 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); @@ -175091,7 +189366,23 @@ emit_jfif_app0 (j_compress_ptr cinfo) 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); @@ -175118,8 +189409,17 @@ emit_adobe_app14 (j_compress_ptr cinfo) } } +/* + * 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); @@ -175131,10 +189431,22 @@ write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen) 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) { @@ -175142,6 +189454,7 @@ write_file_header (j_compress_ptr cinfo) 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 */ @@ -175150,6 +189463,14 @@ write_file_header (j_compress_ptr cinfo) 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) { @@ -175157,12 +189478,19 @@ write_frame_header (j_compress_ptr cinfo) 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; @@ -175175,10 +189503,12 @@ write_frame_header (j_compress_ptr cinfo) } 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 { @@ -175191,6 +189521,12 @@ write_frame_header (j_compress_ptr cinfo) } } +/* + * 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) { @@ -175199,11 +189535,19 @@ write_scan_header (j_compress_ptr cinfo) 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); @@ -175211,12 +189555,16 @@ write_scan_header (j_compress_ptr cinfo) 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; @@ -175225,12 +189573,23 @@ write_scan_header (j_compress_ptr cinfo) 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) { @@ -175255,15 +189614,21 @@ write_tables_only (j_compress_ptr cinfo) 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; @@ -175271,6 +189636,7 @@ jinit_marker_writer (j_compress_ptr cinfo) 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 ***/ @@ -175279,6 +189645,8 @@ jinit_marker_writer (j_compress_ptr cinfo) /*** 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 */ @@ -175298,34 +189666,45 @@ typedef struct { 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; @@ -175339,25 +189718,34 @@ initial_setup (j_compress_ptr cinfo) 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)); @@ -175367,6 +189755,9 @@ initial_setup (j_compress_ptr cinfo) 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; @@ -175375,11 +189766,15 @@ validate_script (j_compress_ptr cinfo) #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 @@ -175398,6 +189793,7 @@ validate_script (j_compress_ptr cinfo) } 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); @@ -175405,15 +189801,24 @@ validate_script (j_compress_ptr cinfo) 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 @@ -175435,9 +189840,11 @@ validate_script (j_compress_ptr cinfo) 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); } @@ -175446,8 +189853,10 @@ validate_script (j_compress_ptr cinfo) } #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]) @@ -175457,8 +189866,14 @@ validate_script (j_compress_ptr cinfo) } } + /* 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); @@ -175476,11 +189891,13 @@ validate_script (j_compress_ptr cinfo) 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; @@ -175497,6 +189914,7 @@ select_scan_parameters (j_compress_ptr cinfo) 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); @@ -175513,35 +189931,46 @@ select_scan_parameters (j_compress_ptr cinfo) 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)); @@ -175553,16 +189982,19 @@ per_scan_setup (j_compress_ptr cinfo) 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); @@ -175573,12 +190005,22 @@ per_scan_setup (j_compress_ptr cinfo) } + /* 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) { @@ -175586,6 +190028,9 @@ prepare_for_pass (j_compress_ptr cinfo) 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) { @@ -175600,13 +190045,16 @@ prepare_for_pass (j_compress_ptr cinfo) 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) { @@ -175615,16 +190063,23 @@ prepare_for_pass (j_compress_ptr cinfo) 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); @@ -175636,12 +190091,23 @@ prepare_for_pass (j_compress_ptr cinfo) 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) { @@ -175651,23 +190117,36 @@ pass_startup (j_compress_ptr 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++; @@ -175677,6 +190156,10 @@ finish_pass_master (j_compress_ptr cinfo) master->pass_number++; } +/* + * Initialize master compression control. + */ + GLOBAL(void) jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) { @@ -175691,6 +190174,7 @@ jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) 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) { @@ -175707,12 +190191,15 @@ jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) 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; @@ -175728,35 +190215,72 @@ jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) /*** 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) { @@ -175784,15 +190308,25 @@ jpeg_alloc_huff_table (j_common_ptr cinfo) /*** 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); @@ -175806,6 +190340,7 @@ jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, 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) @@ -175813,13 +190348,23 @@ jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, (*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, @@ -175841,6 +190386,7 @@ jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, 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, @@ -175849,10 +190395,21 @@ jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, 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 @@ -175863,23 +190420,40 @@ jpeg_quality_scaling (int 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]; @@ -175888,11 +190462,14 @@ add_huff_table (j_compress_ptr cinfo, 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 }; @@ -175964,58 +190541,106 @@ std_huff_tables (j_compress_ptr cinfo) 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) { @@ -176043,6 +190668,10 @@ jpeg_default_colorspace (j_compress_ptr cinfo) } } +/* + * Set the JPEG colorspace, and choose colorspace-dependent default values. + */ + GLOBAL(void) jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { @@ -176058,9 +190687,14 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) 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 */ @@ -176070,6 +190704,7 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE 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: @@ -176082,6 +190717,8 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) 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); @@ -176121,6 +190758,7 @@ jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) 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; @@ -176135,6 +190773,7 @@ fill_a_scan (jpeg_scan_info * scanptr, int ci, 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; @@ -176152,10 +190791,12 @@ fill_scans (jpeg_scan_info * scanptr, int ncomps, 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; @@ -176164,11 +190805,17 @@ fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) 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) { @@ -176176,18 +190823,29 @@ jpeg_simple_progression (j_compress_ptr cinfo) 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 *) @@ -176199,21 +190857,34 @@ jpeg_simple_progression (j_compress_ptr cinfo) 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); } @@ -176228,36 +190899,61 @@ jpeg_simple_progression (j_compress_ptr cinfo) #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) \ @@ -176269,6 +190965,7 @@ typedef phuff_entropy_encoder * phuff_entropy_ptr; #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, @@ -176280,6 +190977,10 @@ METHODDEF(boolean) encode_mcu_AC_refine JPP((j_compress_ptr cinfo, 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) { @@ -176293,6 +190994,9 @@ start_pass_phuff (j_compress_ptr cinfo, boolean 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; @@ -176303,6 +191007,7 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) 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, @@ -176314,9 +191019,14 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) 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; @@ -176325,29 +191035,44 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) 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) \ @@ -176355,22 +191080,35 @@ start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) 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); @@ -176408,6 +191146,10 @@ flush_bits_p (phuff_entropy_ptr entropy) 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) @@ -176420,6 +191162,10 @@ emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol) } } +/* + * Emit bits from a correction bit buffer. + */ + LOCAL(void) emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, unsigned int nbits) @@ -176434,6 +191180,10 @@ emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, } } +/* + * Emit any pending EOBRUN symbol. + */ + LOCAL(void) emit_eobrun (phuff_entropy_ptr entropy) { @@ -176444,6 +191194,7 @@ emit_eobrun (phuff_entropy_ptr entropy) 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); @@ -176453,11 +191204,16 @@ emit_eobrun (phuff_entropy_ptr entropy) 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) { @@ -176472,14 +191228,21 @@ emit_restart_p (phuff_entropy_ptr entropy, int 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) { @@ -176495,36 +191258,52 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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); } @@ -176532,6 +191311,7 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -176544,6 +191324,11 @@ encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -176558,12 +191343,16 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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++) { @@ -176571,34 +191360,48 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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 */ @@ -176613,6 +191416,7 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -176625,6 +191429,12 @@ encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -176637,13 +191447,16 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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); } @@ -176651,6 +191464,7 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -176663,6 +191477,10 @@ encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) return TRUE; } +/* + * MCU encoding for AC successive approximation refinement scan. + */ + METHODDEF(boolean) encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { @@ -176680,15 +191498,24 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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 */ @@ -176697,6 +191524,8 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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 */ @@ -176707,27 +191536,41 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -176737,6 +191580,10 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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); } @@ -176744,6 +191591,7 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) 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; @@ -176756,6 +191604,10 @@ encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) return TRUE; } +/* + * Finish up at the end of a Huffman-compressed progressive scan. + */ + METHODDEF(void) finish_pass_phuff (j_compress_ptr cinfo) { @@ -176764,6 +191616,7 @@ finish_pass_phuff (j_compress_ptr cinfo) 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); @@ -176771,6 +191624,10 @@ finish_pass_phuff (j_compress_ptr cinfo) 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) { @@ -176781,10 +191638,14 @@ finish_pass_gather_phuff (j_compress_ptr cinfo) 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++) { @@ -176809,6 +191670,10 @@ finish_pass_gather_phuff (j_compress_ptr cinfo) } } +/* + * Module initialization routine for progressive Huffman entropy encoding. + */ + GLOBAL(void) jinit_phuff_encoder (j_compress_ptr cinfo) { @@ -176821,6 +191686,7 @@ jinit_phuff_encoder (j_compress_ptr cinfo) 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; @@ -176835,13 +191701,41 @@ jinit_phuff_encoder (j_compress_ptr cinfo) /*** 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 */ @@ -176855,6 +191749,10 @@ typedef struct { 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) { @@ -176863,14 +191761,25 @@ start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) 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) @@ -176883,6 +191792,15 @@ expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION 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, @@ -176897,6 +191815,7 @@ pre_process_data (j_compress_ptr cinfo, 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); @@ -176907,6 +191826,7 @@ pre_process_data (j_compress_ptr cinfo, *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++) { @@ -176915,6 +191835,7 @@ pre_process_data (j_compress_ptr cinfo, } 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, @@ -176922,6 +191843,9 @@ pre_process_data (j_compress_ptr cinfo, 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; @@ -176939,6 +191863,10 @@ pre_process_data (j_compress_ptr cinfo, #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, @@ -176953,6 +191881,7 @@ pre_process_context (j_compress_ptr cinfo, 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); @@ -176960,6 +191889,7 @@ pre_process_context (j_compress_ptr cinfo, 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; @@ -176974,8 +191904,10 @@ pre_process_context (j_compress_ptr cinfo, 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, @@ -176984,12 +191916,14 @@ pre_process_context (j_compress_ptr cinfo, 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; @@ -177000,6 +191934,10 @@ pre_process_context (j_compress_ptr cinfo, } } +/* + * Create the wrapped-around downsampling input buffer needed for context mode. + */ + LOCAL(void) create_context_buffer (j_compress_ptr cinfo) { @@ -177009,6 +191947,9 @@ create_context_buffer (j_compress_ptr cinfo) 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) * @@ -177016,13 +191957,19 @@ create_context_buffer (j_compress_ptr cinfo) 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]; @@ -177034,6 +191981,10 @@ create_context_buffer (j_compress_ptr cinfo) #endif /* CONTEXT_ROWS_SUPPORTED */ +/* + * Initialize preprocessing controller. + */ + GLOBAL(void) jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) { @@ -177050,7 +192001,12 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) 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); @@ -177058,6 +192014,7 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) 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++) { @@ -177075,23 +192032,37 @@ jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) /*** 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) @@ -177112,6 +192083,12 @@ expand_right_edge (JSAMPARRAY image_data, int num_rows, } } +/* + * 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, @@ -177130,6 +192107,13 @@ sep_downsample (j_compress_ptr cinfo, } } +/* + * 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) @@ -177145,6 +192129,10 @@ int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -177166,16 +192154,36 @@ int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -177186,6 +192194,10 @@ h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -177202,6 +192214,12 @@ h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -177212,6 +192230,10 @@ h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -177234,6 +192256,12 @@ h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, #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) @@ -177244,9 +192272,26 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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 */ @@ -177258,6 +192303,7 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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]) + @@ -177272,20 +192318,27 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, 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]) + @@ -177302,6 +192355,12 @@ h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -177313,9 +192372,19 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, 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 */ @@ -177325,6 +192394,7 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, 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++); @@ -177346,6 +192416,7 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, lastcolsum = colsum; colsum = nextcolsum; } + /* Special case for last column */ membersum = GETJSAMPLE(*inptr); neighsum = lastcolsum + (colsum - membersum) + colsum; membersum = membersum * memberscale + neighsum * neighscale; @@ -177356,6 +192427,11 @@ fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, #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) { @@ -177375,6 +192451,7 @@ jinit_downsampler (j_compress_ptr cinfo) 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 && @@ -177418,24 +192495,48 @@ jinit_downsampler (j_compress_ptr cinfo) /*** 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) @@ -177445,16 +192546,23 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, 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]; @@ -177466,6 +192574,9 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, (*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, @@ -177476,6 +192587,10 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, 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) @@ -177488,7 +192603,18 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, 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; @@ -177500,13 +192626,23 @@ jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, } } +/* + * 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 { @@ -177520,15 +192656,31 @@ transencode_master_selection (j_compress_ptr cinfo, 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 */ @@ -177537,8 +192689,10 @@ typedef struct { 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; @@ -177546,9 +192700,14 @@ 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 { @@ -177562,6 +192721,10 @@ start_iMCU_row2 (j_compress_ptr cinfo) coef->MCU_vert_offset = 0; } +/* + * Initialize for a processing pass. + */ + METHODDEF(void) start_pass_coef2 (j_compress_ptr cinfo, J_BUF_MODE pass_mode) { @@ -177574,6 +192737,16 @@ start_pass_coef2 (j_compress_ptr cinfo, J_BUF_MODE pass_mode) 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) { @@ -177588,6 +192761,7 @@ compress_output2 (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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) @@ -177596,10 +192770,12 @@ compress_output2 (j_compress_ptr cinfo, JSAMPIMAGE input_buf) (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]; @@ -177609,12 +192785,20 @@ compress_output2 (j_compress_ptr cinfo, JSAMPIMAGE input_buf) 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]; @@ -177622,19 +192806,31 @@ compress_output2 (j_compress_ptr cinfo, JSAMPIMAGE input_buf) } } } + /* 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) @@ -177650,8 +192846,10 @@ transencode_coef_controller (j_compress_ptr cinfo, 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)); @@ -177666,34 +192864,53 @@ transencode_coef_controller (j_compress_ptr cinfo, /*** 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; } } @@ -177705,32 +192922,47 @@ jpeg_start_decompress (j_decompress_ptr cinfo) 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; @@ -177738,10 +192970,26 @@ output_pass_setup (j_decompress_ptr cinfo) 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) @@ -177755,18 +193003,25 @@ jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, 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) @@ -177780,50 +193035,73 @@ jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE 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) @@ -177840,11 +193118,17 @@ jpeg_finish_output (j_decompress_ptr cinfo) /*** 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); @@ -177852,6 +193136,12 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) 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 */ @@ -177861,8 +193151,10 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) } 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; @@ -177874,29 +193166,50 @@ jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) 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; @@ -177920,6 +193233,7 @@ default_decompress_parms (j_decompress_ptr cinfo) 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; @@ -177933,6 +193247,7 @@ default_decompress_parms (j_decompress_ptr cinfo) cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ } } + /* Always guess RGB is proper output colorspace. */ cinfo->out_color_space = JCS_RGB; break; @@ -177951,6 +193266,7 @@ default_decompress_parms (j_decompress_ptr cinfo) break; } } else { + /* No special markers, assume straight CMYK. */ cinfo->jpeg_color_space = JCS_CMYK; } cinfo->out_color_space = JCS_CMYK; @@ -177962,6 +193278,7 @@ default_decompress_parms (j_decompress_ptr cinfo) break; } + /* Set defaults for other decompression parameters. */ cinfo->scale_num = 1; /* 1:1 scaling */ cinfo->scale_denom = 1; cinfo->output_gamma = 1.0; @@ -177971,6 +193288,7 @@ default_decompress_parms (j_decompress_ptr cinfo) 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; @@ -177979,11 +193297,39 @@ default_decompress_parms (j_decompress_ptr cinfo) #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) { @@ -178002,34 +193348,58 @@ jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) 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: @@ -178047,43 +193417,68 @@ jpeg_consume_input (j_decompress_ptr cinfo) 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; } @@ -178091,12 +193486,21 @@ jpeg_finish_decompress (j_decompress_ptr cinfo) /*** 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 */ @@ -178111,6 +193515,7 @@ typedef enum { 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") @@ -178263,11 +193668,16 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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))) @@ -178300,6 +193710,7 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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)) @@ -178313,6 +193724,7 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") (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))) @@ -178355,6 +193767,8 @@ JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") #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 */ @@ -178367,14 +193781,56 @@ 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) { @@ -178387,6 +193843,7 @@ fill_input_buffer (j_decompress_ptr cinfo) 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; @@ -178399,31 +193856,81 @@ fill_input_buffer (j_decompress_ptr cinfo) 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, @@ -178450,24 +193957,42 @@ jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) /*** 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 @@ -178475,6 +194000,7 @@ typedef struct { 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 @@ -178489,9 +194015,14 @@ METHODDEF(int) decompress_smooth_data 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 { @@ -178505,6 +194036,10 @@ start_iMCU_row3 (j_decompress_ptr cinfo) coef->MCU_vert_offset = 0; } +/* + * Initialize for an input processing pass. + */ + METHODDEF(void) start_input_pass (j_decompress_ptr cinfo) { @@ -178512,12 +194047,17 @@ start_input_pass (j_decompress_ptr cinfo) 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; @@ -178528,6 +194068,16 @@ start_output_pass (j_decompress_ptr cinfo) 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) { @@ -178541,20 +194091,29 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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; @@ -178581,17 +194140,24 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } } } + /* 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) { @@ -178600,6 +194166,13 @@ dummy_consume_data (j_decompress_ptr cinfo) #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) { @@ -178611,18 +194184,25 @@ consume_data (j_decompress_ptr cinfo) 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]; @@ -178634,22 +194214,35 @@ consume_data (j_decompress_ptr cinfo) } } } + /* 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) { @@ -178664,6 +194257,7 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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)) { @@ -178671,22 +194265,28 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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; @@ -178709,12 +194309,29 @@ decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) #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) { @@ -178729,6 +194346,7 @@ smoothing_ok (j_decompress_ptr cinfo) 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, @@ -178738,8 +194356,10 @@ smoothing_ok (j_decompress_ptr cinfo) 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 || @@ -178747,9 +194367,11 @@ smoothing_ok (j_decompress_ptr cinfo) 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) @@ -178761,6 +194383,10 @@ smoothing_ok (j_decompress_ptr cinfo) 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) { @@ -178782,9 +194408,15 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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; @@ -178793,20 +194425,25 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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) @@ -178821,6 +194458,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) (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]; @@ -178831,6 +194469,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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) @@ -178841,18 +194480,28 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) 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) { @@ -178867,6 +194516,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } workspace[1] = (JCOEF) pred; } + /* AC10 */ if ((Al=coef_bits[2]) != 0 && workspace[8] == 0) { num = 36 * Q00 * (DC2 - DC8); if (num >= 0) { @@ -178881,6 +194531,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } workspace[8] = (JCOEF) pred; } + /* AC20 */ if ((Al=coef_bits[3]) != 0 && workspace[16] == 0) { num = 9 * Q00 * (DC2 + DC8 - 2*DC5); if (num >= 0) { @@ -178895,6 +194546,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } workspace[16] = (JCOEF) pred; } + /* AC11 */ if ((Al=coef_bits[4]) != 0 && workspace[9] == 0) { num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9); if (num >= 0) { @@ -178909,6 +194561,7 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } workspace[9] = (JCOEF) pred; } + /* AC02 */ if ((Al=coef_bits[5]) != 0 && workspace[2] == 0) { num = 9 * Q00 * (DC4 + DC6 - 2*DC5); if (num >= 0) { @@ -178923,8 +194576,10 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) } workspace[2] = (JCOEF) pred; } + /* OK, do the IDCT */ (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) workspace, output_ptr, output_col); + /* Advance for next column */ DC1 = DC2; DC2 = DC3; DC4 = DC5; DC5 = DC6; DC7 = DC8; DC8 = DC9; @@ -178942,6 +194597,10 @@ decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) #endif /* BLOCK_SMOOTHING_SUPPORTED */ +/* + * Initialize coefficient buffer controller. + */ + GLOBAL(void) jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { @@ -178957,8 +194616,12 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) 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; @@ -178966,6 +194629,7 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) 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 @@ -178984,6 +194648,7 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { + /* We only need a single-MCU buffer. */ JBLOCKROW buffer; int i; @@ -179005,9 +194670,12 @@ jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) /*** 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 */ @@ -179016,10 +194684,43 @@ typedef struct { 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) { @@ -179042,15 +194743,33 @@ build_ycc_rgb_table (j_decompress_ptr cinfo) (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, @@ -179062,6 +194781,7 @@ ycc_rgb_convert (j_decompress_ptr cinfo, 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; @@ -179079,6 +194799,7 @@ ycc_rgb_convert (j_decompress_ptr cinfo, 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], @@ -179089,6 +194810,13 @@ ycc_rgb_convert (j_decompress_ptr cinfo, } } +/**************** 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, @@ -179114,6 +194842,12 @@ null_convert2 (j_decompress_ptr cinfo, } } +/* + * 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, @@ -179123,6 +194857,12 @@ grayscale_convert2 (j_decompress_ptr cinfo, 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, @@ -179136,12 +194876,20 @@ gray_rgb_convert (j_decompress_ptr cinfo, 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, @@ -179153,6 +194901,7 @@ ycck_cmyk_convert (j_decompress_ptr cinfo, 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; @@ -179171,22 +194920,33 @@ ycck_cmyk_convert (j_decompress_ptr cinfo, 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) { @@ -179199,6 +194959,7 @@ jinit_color_deconverter (j_decompress_ptr cinfo) 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) @@ -179223,12 +194984,18 @@ jinit_color_deconverter (j_decompress_ptr cinfo) 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 @@ -179260,6 +195027,7 @@ jinit_color_deconverter (j_decompress_ptr cinfo) 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; @@ -179281,14 +195049,39 @@ jinit_color_deconverter (j_decompress_ptr cinfo) /*** 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 @@ -179299,6 +195092,9 @@ typedef union { #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 @@ -179307,6 +195103,12 @@ typedef union { #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) { @@ -179319,6 +195121,7 @@ start_pass (j_decompress_ptr cinfo) 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: @@ -179364,6 +195167,13 @@ start_pass (j_decompress_ptr cinfo) 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; @@ -179374,6 +195184,9 @@ start_pass (j_decompress_ptr cinfo) #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]; @@ -179384,9 +195197,17 @@ start_pass (j_decompress_ptr cinfo) #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, @@ -179410,6 +195231,11 @@ start_pass (j_decompress_ptr cinfo) #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] = { @@ -179436,6 +195262,10 @@ start_pass (j_decompress_ptr cinfo) } } +/* + * Initialize IDCT manager. + */ + GLOBAL(void) jinit_inverse_dct (j_decompress_ptr cinfo) { @@ -179451,10 +195281,12 @@ jinit_inverse_dct (j_decompress_ptr cinfo) 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; } } @@ -179469,6 +195301,8 @@ jinit_inverse_dct (j_decompress_ptr cinfo) /*** Start of inlined file: jdhuff.h ***/ +/* Short forms of external names for systems with brain-damaged linkers. */ + #ifndef __jdhuff_h__ #define __jdhuff_h__ @@ -179478,38 +195312,85 @@ jinit_inverse_dct (j_decompress_ptr cinfo) #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; \ @@ -179528,6 +195409,24 @@ typedef struct { /* Bitreading working state within an MCU */ 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)) \ @@ -179543,10 +195442,28 @@ typedef struct { /* Bitreading working state within an MCU */ #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) { \ @@ -179569,6 +195486,7 @@ slowlabel: \ } \ } +/* 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)); @@ -179578,10 +195496,22 @@ EXTERN(int) jpeg_huff_decode /* 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 @@ -179597,22 +195527,35 @@ typedef struct { 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) { @@ -179620,6 +195563,10 @@ start_pass_huff_decoder (j_decompress_ptr cinfo) 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); @@ -179628,33 +195575,49 @@ start_pass_huff_decoder (j_decompress_ptr cinfo) 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) @@ -179667,6 +195630,11 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, 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 = @@ -179674,6 +195642,7 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int 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, @@ -179681,6 +195650,8 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, 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]; @@ -179692,6 +195663,9 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, 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; @@ -179700,15 +195674,23 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, 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 */ @@ -179718,11 +195700,20 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, } 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; @@ -179732,6 +195723,12 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, } } + /* 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]; @@ -179741,6 +195738,21 @@ jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, } } +/* + * 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 @@ -179751,15 +195763,22 @@ 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; @@ -179769,7 +195788,13 @@ jpeg_fill_bit_buffer (bitread_working_state * state, 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)) @@ -179782,28 +195807,50 @@ jpeg_fill_bit_buffer (bitread_working_state * state, } 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; @@ -179812,6 +195859,11 @@ jpeg_fill_bit_buffer (bitread_working_state * state, 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, @@ -179820,9 +195872,15 @@ jpeg_huff_decode (bitread_working_state * state, 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); @@ -179830,9 +195888,12 @@ jpeg_huff_decode (bitread_working_state * state, 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 */ @@ -179841,29 +195902,59 @@ jpeg_huff_decode (bitread_working_state * state, 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) { @@ -179872,23 +195963,33 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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); @@ -179897,14 +195998,18 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } 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); @@ -179916,6 +196021,10 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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) @@ -179926,6 +196035,8 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } 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); @@ -179946,15 +196057,21 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } } + /* 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) { @@ -179968,6 +196085,7 @@ jinit_huff_decoder (j_decompress_ptr cinfo) 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; } @@ -179979,6 +196097,8 @@ jinit_huff_decoder (j_decompress_ptr cinfo) /*** Start of inlined file: jdinput.c ***/ #define JPEG_INTERNALS +/* Private state */ + typedef struct { struct jpeg_input_controller pub; /* public fields */ @@ -179987,25 +196107,35 @@ typedef struct { 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; @@ -180019,31 +196149,46 @@ initial_setup2 (j_decompress_ptr cinfo) 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 @@ -180052,35 +196197,46 @@ initial_setup2 (j_decompress_ptr cinfo) 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)); @@ -180092,16 +196248,19 @@ per_scan_setup2 (j_decompress_ptr cinfo) 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); @@ -180113,6 +196272,27 @@ per_scan_setup2 (j_decompress_ptr cinfo) } } +/* + * 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) { @@ -180122,12 +196302,15 @@ latch_quant_tables (j_decompress_ptr cinfo) 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)); @@ -180136,6 +196319,13 @@ latch_quant_tables (j_decompress_ptr cinfo) } } +/* + * 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) { @@ -180146,12 +196336,28 @@ start_input_pass2 (j_decompress_ptr 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) { @@ -180168,6 +196374,10 @@ consume_markers (j_decompress_ptr cinfo) 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! */ @@ -180180,6 +196390,9 @@ consume_markers (j_decompress_ptr cinfo) 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; } @@ -180191,6 +196404,10 @@ consume_markers (j_decompress_ptr cinfo) return val; } +/* + * Reset state to begin a fresh datastream. + */ + METHODDEF(void) reset_input_controller (j_decompress_ptr cinfo) { @@ -180200,24 +196417,36 @@ reset_input_controller (j_decompress_ptr cinfo) 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; @@ -180228,14 +196457,110 @@ jinit_input_controller (j_decompress_ptr cinfo) /*** 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 */ @@ -180246,10 +196571,12 @@ typedef struct { 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)); @@ -180264,6 +196591,9 @@ METHODDEF(void) process_data_crank_post 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; @@ -180271,6 +196601,9 @@ alloc_funny_pointers (j_decompress_ptr cinfo) 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)); @@ -180280,6 +196613,9 @@ alloc_funny_pointers (j_decompress_ptr cinfo) 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)); @@ -180292,6 +196628,12 @@ alloc_funny_pointers (j_decompress_ptr cinfo) 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; @@ -180305,14 +196647,21 @@ make_funny_pointers (j_decompress_ptr cinfo) 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]; } @@ -180321,6 +196670,9 @@ make_funny_pointers (j_decompress_ptr cinfo) 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; @@ -180345,6 +196697,10 @@ set_wraparound_pointers (j_decompress_ptr cinfo) 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; @@ -180353,13 +196709,21 @@ set_bottom_pointers (j_decompress_ptr cinfo) 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]; @@ -180367,6 +196731,10 @@ set_bottom_pointers (j_decompress_ptr cinfo) } } +/* + * Initialize for a processing pass. + */ + METHODDEF(void) start_pass_main2 (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) { @@ -180381,6 +196749,7 @@ start_pass_main2 (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) 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 */ @@ -180388,6 +196757,7 @@ start_pass_main2 (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) 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 @@ -180397,6 +196767,11 @@ start_pass_main2 (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) } } +/* + * 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, @@ -180405,24 +196780,37 @@ process_data_simple_main2 (j_decompress_ptr cinfo, 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, @@ -180430,6 +196818,7 @@ process_data_context_main (j_decompress_ptr cinfo, { 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])) @@ -180438,8 +196827,14 @@ process_data_context_main (j_decompress_ptr cinfo, 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); @@ -180448,28 +196843,45 @@ process_data_context_main (j_decompress_ptr cinfo, 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) @@ -180484,6 +196896,10 @@ process_data_crank_post (j_decompress_ptr cinfo, #endif /* QUANT_2PASS_SUPPORTED */ +/* + * Initialize main buffer controller. + */ + GLOBAL(void) jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { @@ -180500,6 +196916,9 @@ jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) 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); @@ -180525,34 +196944,55 @@ jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) /*** 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)) \ @@ -180560,11 +197000,17 @@ typedef my_marker_reader * my_marker_ptr2; 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--; \ @@ -180573,8 +197019,39 @@ typedef my_marker_reader * my_marker_ptr2; 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; @@ -180583,6 +197060,8 @@ get_soi (j_decompress_ptr cinfo) 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; @@ -180590,6 +197069,8 @@ get_soi (j_decompress_ptr cinfo) } cinfo->restart_interval = 0; + /* Set initial assumptions for colorspace etc */ + cinfo->jpeg_color_space = JCS_UNKNOWN; cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ @@ -180609,6 +197090,7 @@ get_soi (j_decompress_ptr cinfo) LOCAL(boolean) get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ { INT32 length; int c, ci; @@ -180634,6 +197116,9 @@ get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) 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); @@ -180668,6 +197153,7 @@ get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) LOCAL(boolean) get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ { INT32 length; int i, ci, n, c, cc; @@ -180688,6 +197174,8 @@ get_sos (j_decompress_ptr cinfo) 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); @@ -180710,6 +197198,7 @@ get_sos (j_decompress_ptr cinfo) 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); @@ -180721,8 +197210,10 @@ get_sos (j_decompress_ptr cinfo) 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); @@ -180733,6 +197224,7 @@ get_sos (j_decompress_ptr cinfo) LOCAL(boolean) get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ { INT32 length; int index, val; @@ -180777,6 +197269,7 @@ get_dac (j_decompress_ptr cinfo) LOCAL(boolean) get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ { INT32 length; UINT8 bits[17]; @@ -180809,6 +197302,9 @@ get_dht (j_decompress_ptr cinfo) 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); @@ -180843,6 +197339,7 @@ get_dht (j_decompress_ptr cinfo) LOCAL(boolean) get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ { INT32 length; int n, i, prec; @@ -180872,6 +197369,7 @@ get_dqt (j_decompress_ptr cinfo) 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; } @@ -180898,6 +197396,7 @@ get_dqt (j_decompress_ptr cinfo) LOCAL(boolean) get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ { INT32 length; unsigned int tmp; @@ -180918,6 +197417,13 @@ get_dri (j_decompress_ptr 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!! */ @@ -180925,6 +197431,10 @@ get_dri (j_decompress_ptr cinfo) 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; @@ -180934,18 +197444,27 @@ examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, 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])); @@ -180959,6 +197478,10 @@ examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, 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); @@ -180975,6 +197498,7 @@ examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, break; } } else { + /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen); } } @@ -180982,6 +197506,10 @@ examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, 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; @@ -180991,6 +197519,7 @@ examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, 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]); @@ -180999,12 +197528,14 @@ examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, 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]; @@ -181014,6 +197545,7 @@ get_interesting_appn (j_decompress_ptr 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) @@ -181024,6 +197556,7 @@ get_interesting_appn (j_decompress_ptr cinfo) 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); @@ -181032,10 +197565,12 @@ get_interesting_appn (j_decompress_ptr cinfo) 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); @@ -181047,6 +197582,7 @@ get_interesting_appn (j_decompress_ptr cinfo) 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; @@ -181056,9 +197592,11 @@ save_marker (j_decompress_ptr cinfo) 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; @@ -181066,6 +197604,7 @@ save_marker (j_decompress_ptr cinfo) 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); @@ -181073,16 +197612,19 @@ save_marker (j_decompress_ptr cinfo) 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; @@ -181091,7 +197633,9 @@ save_marker (j_decompress_ptr cinfo) 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--; @@ -181099,7 +197643,9 @@ save_marker (j_decompress_ptr cinfo) } } + /* 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 { @@ -181108,11 +197654,14 @@ save_marker (j_decompress_ptr cinfo) 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); @@ -181126,6 +197675,7 @@ save_marker (j_decompress_ptr cinfo) 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); @@ -181137,6 +197687,7 @@ save_marker (j_decompress_ptr cinfo) METHODDEF(boolean) skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ { INT32 length; INPUT_VARS(cinfo); @@ -181153,6 +197704,15 @@ skip_variable (j_decompress_ptr cinfo) 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) { @@ -181161,16 +197721,29 @@ next_marker (j_decompress_ptr 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); } @@ -181188,6 +197761,12 @@ next_marker (j_decompress_ptr cinfo) 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); @@ -181203,10 +197782,20 @@ first_marker (j_decompress_ptr 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)) @@ -181216,6 +197805,10 @@ read_markers (j_decompress_ptr 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)) @@ -181243,6 +197836,7 @@ read_markers (j_decompress_ptr cinfo) 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 */ @@ -181330,16 +197924,36 @@ read_markers (j_decompress_ptr cinfo) 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; @@ -181347,27 +197961,82 @@ read_restart_marker (j_decompress_ptr cinfo) 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 */ @@ -181386,19 +198055,27 @@ jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) 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) { @@ -181413,19 +198090,30 @@ reset_marker_reader (j_decompress_ptr cinfo) 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++) { @@ -181434,9 +198122,14 @@ jinit_marker_reader (j_decompress_ptr cinfo) } 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) @@ -181447,18 +198140,26 @@ jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, 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; } @@ -181475,6 +198176,10 @@ jpeg_save_markers (j_decompress_ptr cinfo, int 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) @@ -181494,6 +198199,8 @@ jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, /*** Start of inlined file: jdmaster.c ***/ #define JPEG_INTERNALS +/* Private state */ + typedef struct { struct jpeg_decomp_master pub; /* public fields */ @@ -181501,22 +198208,33 @@ typedef struct { 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 || @@ -181524,52 +198242,73 @@ use_merged_upsample (j_decompress_ptr cinfo) 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; @@ -181583,8 +198322,12 @@ jpeg_calc_output_dimensions (j_decompress_ptr cinfo) 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), @@ -181597,11 +198340,17 @@ jpeg_calc_output_dimensions (j_decompress_ptr cinfo) #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; @@ -181625,14 +198374,59 @@ jpeg_calc_output_dimensions (j_decompress_ptr cinfo) 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; @@ -181642,18 +198436,33 @@ prepare_range_limit_table (j_decompress_ptr cinfo) (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) { @@ -181662,19 +198471,24 @@ master_selection (j_decompress_ptr cinfo) 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; @@ -181683,6 +198497,7 @@ master_selection (j_decompress_ptr cinfo) 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; @@ -181705,6 +198520,7 @@ master_selection (j_decompress_ptr cinfo) #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); @@ -181713,8 +198529,12 @@ master_selection (j_decompress_ptr cinfo) 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 @@ -181728,7 +198548,9 @@ master_selection (j_decompress_ptr 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 { @@ -181742,34 +198564,54 @@ master_selection (j_decompress_ptr cinfo) 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) { @@ -181777,6 +198619,7 @@ prepare_for_output_pass (j_decompress_ptr cinfo) 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); @@ -181786,6 +198629,7 @@ prepare_for_output_pass (j_decompress_ptr cinfo) #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; @@ -181809,16 +198653,24 @@ prepare_for_output_pass (j_decompress_ptr cinfo) } } + /* 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) { @@ -181831,17 +198683,24 @@ finish_output_pass (j_decompress_ptr cinfo) #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 @@ -181850,6 +198709,11 @@ jpeg_new_colormap (j_decompress_ptr cinfo) #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) { @@ -181875,18 +198739,27 @@ jinit_master_decompress (j_decompress_ptr cinfo) #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 */ @@ -181900,6 +198773,11 @@ typedef my_upsampler * my_upsample_ptr; #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) { @@ -181922,47 +198800,72 @@ build_ycc_rgb_table2 (j_decompress_ptr cinfo) (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]; @@ -181970,11 +198873,14 @@ merged_2v_upsample (j_decompress_ptr cinfo, 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)++; } @@ -181985,15 +198891,31 @@ merged_1v_upsample (j_decompress_ptr cinfo, 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, @@ -182005,6 +198927,7 @@ h2v1_merged_upsample (j_decompress_ptr cinfo, 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; @@ -182016,12 +198939,15 @@ h2v1_merged_upsample (j_decompress_ptr cinfo, 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]; @@ -182033,6 +198959,7 @@ h2v1_merged_upsample (j_decompress_ptr cinfo, 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); @@ -182046,6 +198973,10 @@ h2v1_merged_upsample (j_decompress_ptr cinfo, } } +/* + * 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, @@ -182057,6 +198988,7 @@ h2v2_merged_upsample (j_decompress_ptr cinfo, 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; @@ -182070,12 +199002,15 @@ h2v2_merged_upsample (j_decompress_ptr cinfo, 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]; @@ -182097,6 +199032,7 @@ h2v2_merged_upsample (j_decompress_ptr cinfo, 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); @@ -182114,6 +199050,14 @@ h2v2_merged_upsample (j_decompress_ptr cinfo, } } +/* + * 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) { @@ -182131,12 +199075,14 @@ jinit_merged_upsampler (j_decompress_ptr cinfo) 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; } @@ -182154,11 +199100,23 @@ jinit_merged_upsampler (j_decompress_ptr cinfo) #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 @@ -182175,11 +199133,16 @@ typedef struct { 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 */ @@ -182187,6 +199150,7 @@ typedef struct { 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, @@ -182196,6 +199160,10 @@ METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, 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) { @@ -182207,25 +199175,39 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) 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]; @@ -182239,6 +199221,7 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) } } + /* Select MCU decoding routine */ if (cinfo->Ah == 0) { if (is_DC_band) entropy->pub.decode_mcu = decode_mcu_DC_first; @@ -182253,6 +199236,9 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) 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; @@ -182263,44 +199249,87 @@ start_pass_phuff_decoder (j_decompress_ptr cinfo) 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) { @@ -182314,23 +199343,33 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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); @@ -182338,20 +199377,29 @@ decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -182364,16 +199412,25 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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 { @@ -182390,6 +199447,7 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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 */ @@ -182410,14 +199468,22 @@ decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -182427,29 +199493,45 @@ decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -182466,22 +199548,34 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) 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) { @@ -182507,7 +199601,12 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } 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) { @@ -182528,13 +199627,20 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } 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) { @@ -182549,24 +199655,32 @@ decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) } } } + /* 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) { @@ -182580,10 +199694,12 @@ jinit_phuff_decoder (j_decompress_ptr cinfo) 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)); @@ -182601,18 +199717,27 @@ jinit_phuff_decoder (j_decompress_ptr cinfo) /*** 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, @@ -182634,6 +199759,10 @@ METHODDEF(void) post_process_2pass JDIMENSION out_rows_avail)); #endif +/* + * Initialize for a processing pass. + */ + METHODDEF(void) start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) { @@ -182642,23 +199771,33 @@ start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) 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; @@ -182671,6 +199810,11 @@ start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) 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, @@ -182681,6 +199825,8 @@ post_process_1pass (j_decompress_ptr cinfo, 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; @@ -182688,6 +199834,7 @@ post_process_1pass (j_decompress_ptr cinfo, (*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; @@ -182695,6 +199842,10 @@ post_process_1pass (j_decompress_ptr cinfo, #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, @@ -182705,17 +199856,21 @@ post_process_prepass (j_decompress_ptr cinfo, 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, @@ -182723,12 +199878,17 @@ post_process_prepass (j_decompress_ptr cinfo, *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, @@ -182739,25 +199899,30 @@ post_process_2pass (j_decompress_ptr cinfo, 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; @@ -182767,6 +199932,10 @@ post_process_2pass (j_decompress_ptr cinfo, #endif /* QUANT_2PASS_SUPPORTED */ +/* + * Initialize postprocessing controller. + */ + GLOBAL(void) jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) { @@ -182780,9 +199949,16 @@ jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) 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, @@ -182794,6 +199970,7 @@ jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) 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, @@ -182808,37 +199985,66 @@ jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) /*** 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, @@ -182851,9 +200057,13 @@ sep_upsample (j_decompress_ptr cinfo, 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); @@ -182861,9 +200071,16 @@ sep_upsample (j_decompress_ptr cinfo, 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; @@ -182873,13 +200090,27 @@ sep_upsample (j_decompress_ptr cinfo, 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) @@ -182887,6 +200118,11 @@ fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, *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) @@ -182894,6 +200130,17 @@ noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, *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) @@ -182912,6 +200159,7 @@ int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -182921,6 +200169,7 @@ int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, *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); @@ -182930,6 +200179,11 @@ int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -182952,6 +200206,11 @@ h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -182979,6 +200238,21 @@ h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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) @@ -182992,22 +200266,33 @@ h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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) @@ -183025,6 +200310,7 @@ h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -183032,6 +200318,7 @@ h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -183039,12 +200326,15 @@ h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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); } @@ -183052,6 +200342,10 @@ h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * Module initialization routine for upsampling. + */ + GLOBAL(void) jinit_upsampler (j_decompress_ptr cinfo) { @@ -183072,10 +200366,19 @@ jinit_upsampler (j_decompress_ptr cinfo) 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) / @@ -183085,19 +200388,23 @@ jinit_upsampler (j_decompress_ptr cinfo) 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; @@ -183105,6 +200412,7 @@ jinit_upsampler (j_decompress_ptr cinfo) 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); @@ -183126,47 +200434,89 @@ jinit_upsampler (j_decompress_ptr cinfo) /*** 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 { @@ -183180,17 +200530,24 @@ transdecode_master_selection (j_decompress_ptr cinfo) 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; @@ -183209,10 +200566,18 @@ transdecode_master_selection (j_decompress_ptr cinfo) #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) { @@ -183222,6 +200587,8 @@ jpeg_fdct_float (FAST_FLOAT * data) FAST_FLOAT *dataptr; int ctr; + /* Pass 1: process rows. */ + dataptr = data; for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { tmp0 = dataptr[0] + dataptr[7]; @@ -183233,6 +200600,8 @@ jpeg_fdct_float (FAST_FLOAT * data) tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; + /* Even part */ + tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; @@ -183245,10 +200614,13 @@ jpeg_fdct_float (FAST_FLOAT * data) 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 */ @@ -183265,6 +200637,8 @@ jpeg_fdct_float (FAST_FLOAT * data) 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]; @@ -183276,6 +200650,8 @@ jpeg_fdct_float (FAST_FLOAT * data) 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; @@ -183288,10 +200664,13 @@ jpeg_fdct_float (FAST_FLOAT * data) 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 */ @@ -183318,10 +200697,48 @@ jpeg_fdct_float (FAST_FLOAT * data) #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 @@ -183330,6 +200747,13 @@ jpeg_fdct_float (FAST_FLOAT * data) #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) */ @@ -183358,12 +200782,23 @@ jpeg_fdct_float (FAST_FLOAT * data) #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) { @@ -183374,6 +200809,10 @@ jpeg_fdct_islow (DCTELEM * data) 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]; @@ -183385,6 +200824,10 @@ jpeg_fdct_islow (DCTELEM * data) 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; @@ -183399,6 +200842,11 @@ jpeg_fdct_islow (DCTELEM * data) 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; @@ -183425,6 +200873,11 @@ jpeg_fdct_islow (DCTELEM * data) 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]; @@ -183436,6 +200889,10 @@ jpeg_fdct_islow (DCTELEM * data) 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; @@ -183450,6 +200907,11 @@ jpeg_fdct_islow (DCTELEM * data) 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; @@ -183493,12 +200955,41 @@ jpeg_fdct_islow (DCTELEM * data) #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) */ @@ -183511,13 +201002,26 @@ jpeg_fdct_islow (DCTELEM * data) #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) { @@ -183528,6 +201032,8 @@ jpeg_fdct_ifast (DCTELEM * data) int ctr; SHIFT_TEMPS + /* Pass 1: process rows. */ + dataptr = data; for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { tmp0 = dataptr[0] + dataptr[7]; @@ -183539,6 +201045,8 @@ jpeg_fdct_ifast (DCTELEM * data) tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; + /* Even part */ + tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; @@ -183551,10 +201059,13 @@ jpeg_fdct_ifast (DCTELEM * data) 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 */ @@ -183571,6 +201082,8 @@ jpeg_fdct_ifast (DCTELEM * data) 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]; @@ -183582,6 +201095,8 @@ jpeg_fdct_ifast (DCTELEM * data) 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; @@ -183594,10 +201109,13 @@ jpeg_fdct_ifast (DCTELEM * data) 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 */ @@ -183626,12 +201144,24 @@ jpeg_fdct_ifast (DCTELEM * data) #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, @@ -183649,15 +201179,26 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -183675,6 +201216,8 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -183691,6 +201234,8 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -183726,9 +201271,19 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -183741,6 +201296,8 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, tmp1 = tmp11 + tmp12; tmp2 = tmp11 - tmp12; + /* Odd part */ + z13 = wsptr[5] + wsptr[3]; z10 = wsptr[5] - wsptr[3]; z11 = wsptr[1] + wsptr[7]; @@ -183757,6 +201314,8 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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) @@ -183793,10 +201352,38 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 @@ -183805,6 +201392,13 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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) */ @@ -183817,13 +201411,28 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 @@ -183831,6 +201440,10 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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 @@ -183853,6 +201466,10 @@ jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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, @@ -183871,15 +201488,26 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -183897,6 +201525,8 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -183913,6 +201543,8 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -183948,13 +201580,25 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -183972,6 +201616,8 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, } #endif + /* Even part */ + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); @@ -183984,6 +201630,8 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -184000,6 +201648,8 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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) @@ -184035,10 +201685,46 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 @@ -184047,6 +201733,13 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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) */ @@ -184075,14 +201768,30 @@ jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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, @@ -184100,15 +201809,28 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -184126,6 +201848,9 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -184144,6 +201869,10 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -184172,6 +201901,8 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -184186,13 +201917,25 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -184210,6 +201953,9 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, } #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]; @@ -184225,6 +201971,10 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -184253,6 +202003,8 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -184292,10 +202044,16 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 @@ -184304,6 +202062,13 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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) */ @@ -184336,14 +202101,31 @@ jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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, @@ -184360,15 +202142,19 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -184379,6 +202165,8 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, continue; } + /* Even part */ + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); tmp0 <<= (CONST_BITS+1); @@ -184390,6 +202178,8 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -184405,19 +202195,25 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + 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]; @@ -184431,6 +202227,8 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, } #endif + /* Even part */ + tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) @@ -184439,6 +202237,8 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, tmp10 = tmp0 + tmp2; tmp12 = tmp0 - tmp2; + /* Odd part */ + z1 = (INT32) wsptr[7]; z2 = (INT32) wsptr[5]; z3 = (INT32) wsptr[3]; @@ -184454,6 +202254,8 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + 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]; @@ -184471,6 +202273,11 @@ jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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, @@ -184486,14 +202293,18 @@ jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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; @@ -184502,9 +202313,13 @@ jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]); @@ -184514,16 +202329,22 @@ jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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]; @@ -184535,13 +202356,19 @@ jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, } #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]; @@ -184553,6 +202380,11 @@ jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, } } +/* + * 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, @@ -184563,6 +202395,9 @@ jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, 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); @@ -184583,6 +202418,8 @@ jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 @@ -184594,24 +202431,85 @@ jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, #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 */ @@ -184634,6 +202532,7 @@ typedef union { //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, @@ -184645,25 +202544,49 @@ typedef struct backing_store_struct { 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)); @@ -184678,10 +202601,53 @@ 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 { @@ -184704,22 +202670,43 @@ typedef union large_pool_struct { 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 */ @@ -184761,6 +202748,10 @@ print_mem_stats (j_common_ptr cinfo, int pool_id) 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); @@ -184782,6 +202773,8 @@ print_mem_stats (j_common_ptr cinfo, int pool_id) 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 */ @@ -184789,6 +202782,19 @@ out_of_memory (j_common_ptr cinfo, int which) 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 */ @@ -184805,19 +202811,23 @@ static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = 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; @@ -184829,14 +202839,18 @@ alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) 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) @@ -184846,6 +202860,7 @@ alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) 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; @@ -184855,6 +202870,7 @@ alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) 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; @@ -184863,20 +202879,38 @@ alloc_small (j_common_ptr cinfo, int pool_id, size_t 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 */ @@ -184886,7 +202920,11 @@ alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) 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; @@ -184894,9 +202932,23 @@ alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) 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; @@ -184904,6 +202956,7 @@ alloc_sarray (j_common_ptr cinfo, int pool_id, 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) @@ -184914,9 +202967,11 @@ alloc_sarray (j_common_ptr cinfo, int pool_id, 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); @@ -184932,9 +202987,15 @@ alloc_sarray (j_common_ptr cinfo, int pool_id, 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; @@ -184942,6 +203003,7 @@ alloc_barray (j_common_ptr cinfo, int pool_id, 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) @@ -184952,9 +203014,11 @@ alloc_barray (j_common_ptr cinfo, int pool_id, 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); @@ -184970,17 +203034,56 @@ alloc_barray (j_common_ptr cinfo, int pool_id, 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)); @@ -185000,13 +203103,16 @@ 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)); @@ -185024,6 +203130,7 @@ request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, 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; @@ -185031,6 +203138,10 @@ realize_virt_arrays (j_common_ptr cinfo) 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) { @@ -185053,23 +203164,35 @@ realize_virt_arrays (j_common_ptr cinfo) 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 * @@ -185090,8 +203213,10 @@ realize_virt_arrays (j_common_ptr cinfo) 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 * @@ -185111,15 +203236,20 @@ realize_virt_arrays (j_common_ptr cinfo) 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; @@ -185138,15 +203268,20 @@ do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) 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; @@ -185167,25 +203302,39 @@ 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; @@ -185193,8 +203342,16 @@ access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, 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 */ @@ -185218,8 +203375,10 @@ access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, 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); } @@ -185227,25 +203386,39 @@ 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; @@ -185253,8 +203426,16 @@ access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, 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 */ @@ -185278,11 +203459,17 @@ access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, 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) { @@ -185299,6 +203486,7 @@ free_pool (j_common_ptr cinfo, int pool_id) 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; @@ -185319,6 +203507,7 @@ free_pool (j_common_ptr cinfo, int pool_id) mem->virt_barray_list = NULL; } + /* Release large objects */ lhdr_ptr = mem->large_list[pool_id]; mem->large_list[pool_id] = NULL; @@ -185332,6 +203521,7 @@ free_pool (j_common_ptr cinfo, int pool_id) lhdr_ptr = next_lhdr_ptr; } + /* Release small objects */ shdr_ptr = mem->small_list[pool_id]; mem->small_list[pool_id] = NULL; @@ -185346,21 +203536,36 @@ free_pool (j_common_ptr cinfo, int pool_id) } } +/* + * 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) { @@ -185371,8 +203576,20 @@ jinit_memory_mgr (j_common_ptr cinfo) 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) @@ -185380,6 +203597,7 @@ jinit_memory_mgr (j_common_ptr cinfo) 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) { @@ -185387,6 +203605,7 @@ jinit_memory_mgr (j_common_ptr cinfo) 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; @@ -185399,8 +203618,10 @@ jinit_memory_mgr (j_common_ptr cinfo) 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--) { @@ -185412,8 +203633,15 @@ jinit_memory_mgr (j_common_ptr cinfo) 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; @@ -185441,6 +203669,11 @@ 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) { @@ -185453,6 +203686,13 @@ 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) { @@ -185465,6 +203705,11 @@ 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) @@ -185472,6 +203717,12 @@ jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, 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) @@ -185479,6 +203730,11 @@ jpeg_open_backing_store (j_common_ptr cinfo, struct backing_store_struct *info, 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) { @@ -185488,6 +203744,7 @@ jpeg_mem_init (j_common_ptr cinfo) GLOBAL(void) jpeg_mem_term (j_common_ptr cinfo) { + /* no work */ } /*** End of inlined file: jmemnobs.c ***/ @@ -185497,7 +203754,58 @@ jpeg_mem_term (j_common_ptr cinfo) #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 */ @@ -185505,6 +203813,10 @@ 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 }, @@ -185523,6 +203835,30 @@ static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { { 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 */ @@ -185533,30 +203869,56 @@ typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ 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; @@ -185565,6 +203927,8 @@ select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) 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++; @@ -185574,18 +203938,27 @@ select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) } 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) @@ -185601,16 +203974,30 @@ select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) 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) { @@ -185619,8 +204006,10 @@ create_colormap (j_decompress_ptr cinfo) 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], @@ -185628,18 +204017,28 @@ create_colormap (j_decompress_ptr cinfo) 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; } @@ -185647,10 +204046,17 @@ create_colormap (j_decompress_ptr cinfo) 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) { @@ -185658,6 +204064,11 @@ create_colorindex (j_decompress_ptr cinfo) 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; @@ -185671,23 +204082,30 @@ create_colorindex (j_decompress_ptr cinfo) (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]; @@ -185696,6 +204114,11 @@ create_colorindex (j_decompress_ptr cinfo) } } +/* + * 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) { @@ -185706,17 +204129,31 @@ make_odither_array (j_decompress_ptr cinfo, int ncolors) 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) { @@ -185739,9 +204176,14 @@ create_odither_tables (j_decompress_ptr cinfo) } } +/* + * 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; @@ -185768,6 +204210,7 @@ color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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; @@ -185794,6 +204237,7 @@ color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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; @@ -185808,6 +204252,7 @@ quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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; @@ -185819,12 +204264,20 @@ quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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; } @@ -185833,6 +204286,7 @@ quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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; @@ -185876,6 +204330,7 @@ quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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 */ @@ -185900,34 +204355,59 @@ quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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 */ @@ -185936,16 +204416,28 @@ quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, 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) { @@ -185960,6 +204452,10 @@ alloc_fs_workspace (j_decompress_ptr cinfo) } } +/* + * Initialize for one-pass color quantization. + */ + METHODDEF(void) start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) { @@ -185967,9 +204463,11 @@ start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) 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) @@ -185983,16 +204481,23 @@ start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) 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); @@ -186003,17 +204508,31 @@ start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) } } +/* + * 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) { @@ -186029,14 +204548,23 @@ jinit_1pass_quantizer (j_decompress_ptr cinfo) 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); } @@ -186050,10 +204578,63 @@ jinit_1pass_quantizer (j_decompress_ptr cinfo) #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 @@ -186070,16 +204651,47 @@ jinit_1pass_quantizer (j_decompress_ptr cinfo) #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< 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; @@ -186144,11 +204796,21 @@ prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, } } +/* + * 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; @@ -186156,6 +204818,8 @@ 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; @@ -186173,6 +204837,8 @@ find_biggest_color_pop (boxptr boxlist, int numboxes) 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; @@ -186190,6 +204856,8 @@ find_biggest_volume (boxptr boxlist, int numboxes) 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; @@ -186270,11 +204938,20 @@ update_box (j_decompress_ptr cinfo, boxptr boxp) } 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++) { @@ -186290,12 +204967,16 @@ update_box (j_decompress_ptr cinfo, boxptr boxp) 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 { @@ -186304,11 +204985,19 @@ median_cut (j_decompress_ptr cinfo, boxptr boxlist, int 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; } @@ -186318,6 +205007,12 @@ median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, 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; @@ -186335,6 +205030,7 @@ median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, b2->c2min = lb+1; break; } + /* Update stats for boxes */ update_box(cinfo, b1); update_box(cinfo, b2); numboxes++; @@ -186344,7 +205040,10 @@ median_cut (j_decompress_ptr cinfo, boxptr boxlist, int 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; @@ -186380,13 +205079,16 @@ compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) 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; @@ -186394,14 +205096,71 @@ select_colors (j_decompress_ptr cinfo, int desired_colors) 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) @@ -186414,9 +205173,25 @@ select_colors (j_decompress_ptr cinfo, int desired_colors) #define BOX_C1_SHIFT (C1_SHIFT + BOX_C1_LOG) #define BOX_C2_SHIFT (C2_SHIFT + BOX_C2_LOG) +/* + * The next three routines implement inverse colormap filling. They could + * all be folded into one big routine, but splitting them up this way saves + * some stack space (the mindist[] and bestdist[] arrays need not coexist) + * and may allow some compilers to produce better code by registerizing more + * inner-loop variables. + */ + LOCAL(int) find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, JSAMPLE colorlist[]) +/* Locate the colormap entries close enough to an update box to be candidates + * for the nearest entry to some cell(s) in the update box. The update box + * is specified by the center coordinates of its first cell. The number of + * candidate colormap entries is returned, and their colormap indexes are + * placed in colorlist[]. + * This routine uses Heckbert's "locally sorted search" criterion to select + * the colors that need further consideration. + */ { int numcolors = cinfo->actual_number_of_colors; int maxc0, maxc1, maxc2; @@ -186425,6 +205200,12 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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)); @@ -186432,9 +205213,18 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186447,6 +205237,7 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186469,6 +205260,7 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186490,6 +205282,7 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186504,6 +205297,10 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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) @@ -186515,6 +205312,12 @@ find_nearby_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186525,27 +205328,38 @@ find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186576,6 +205390,9 @@ find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, 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; @@ -186583,23 +205400,35 @@ fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) 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; @@ -186614,9 +205443,14 @@ fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) } } +/* + * 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; @@ -186631,12 +205465,16 @@ pass2_no_dither (j_decompress_ptr cinfo, 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); } } @@ -186645,6 +205483,7 @@ pass2_no_dither (j_decompress_ptr cinfo, 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; @@ -186671,6 +205510,7 @@ pass2_fs_dither (j_decompress_ptr cinfo, 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; @@ -186678,37 +205518,64 @@ pass2_fs_dither (j_decompress_ptr cinfo, 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 */ @@ -186736,18 +205603,44 @@ pass2_fs_dither (j_decompress_ptr cinfo, 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; @@ -186759,34 +205652,48 @@ init_error_limit (j_decompress_ptr cinfo) 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) { @@ -186794,20 +205701,25 @@ start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) 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); @@ -186817,16 +205729,20 @@ start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) 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], @@ -186836,14 +205752,23 @@ start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) } } +/* + * 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) { @@ -186859,9 +205784,11 @@ jinit_2pass_quantizer (j_decompress_ptr cinfo) 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++) { @@ -186871,10 +205798,17 @@ jinit_2pass_quantizer (j_decompress_ptr cinfo) } 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) @@ -186883,13 +205817,21 @@ jinit_2pass_quantizer (j_decompress_ptr cinfo) } 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); } } @@ -186901,6 +205843,11 @@ jinit_2pass_quantizer (j_decompress_ptr cinfo) /*** 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] = { @@ -186916,6 +205863,20 @@ const int jpeg_zigzag_order[DCTSIZE2] = { #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, @@ -186929,19 +205890,36 @@ const int jpeg_natural_order[DCTSIZE2+16] = { 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) @@ -186956,6 +205934,11 @@ 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 @@ -186983,6 +205966,7 @@ jcopy_sample_rows (JSAMPARRAY input_array, int source_row, 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))); @@ -187000,6 +205984,8 @@ jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, 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); @@ -187016,14 +206002,20 @@ jzero_far (void FAR * target, size_t bytestozero) /*** 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 @@ -187032,6 +206024,10 @@ jzero_far (void FAR * target, size_t bytestozero) #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 */ @@ -187043,23 +206039,63 @@ typedef enum { 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, @@ -187067,6 +206103,10 @@ EXTERN(void) jtransform_execute_transformation #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 */ @@ -187075,8 +206115,10 @@ typedef enum { #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)); @@ -187086,9 +206128,47 @@ EXTERN(void) jcopy_markers_execute #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; @@ -187097,6 +206177,11 @@ do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187111,6 +206196,7 @@ do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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; @@ -187131,6 +206217,7 @@ 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; @@ -187139,6 +206226,13 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187150,17 +206244,20 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, ((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; @@ -187168,13 +206265,16 @@ do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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); } @@ -187187,6 +206287,7 @@ 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; @@ -187194,6 +206295,11 @@ do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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; @@ -187224,6 +206330,11 @@ 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; @@ -187231,6 +206342,10 @@ do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187250,6 +206365,7 @@ do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187260,6 +206376,7 @@ do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) @@ -187276,6 +206393,11 @@ 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; @@ -187283,6 +206405,10 @@ do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187302,6 +206428,7 @@ do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187312,6 +206439,7 @@ do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } } 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++) @@ -187328,6 +206456,11 @@ 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; @@ -187349,33 +206482,40 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, ((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]; @@ -187387,8 +206527,10 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } } 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]; @@ -187397,6 +206539,7 @@ do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, *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]; @@ -187413,6 +206556,15 @@ 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; @@ -187443,6 +206595,7 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) { @@ -187459,6 +206612,7 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } } 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++) { @@ -187471,6 +206625,7 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } 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++) { @@ -187481,6 +206636,7 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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++) @@ -187494,6 +206650,16 @@ do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, } } +/* 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) @@ -187505,17 +206671,24 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, 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); @@ -187534,6 +206707,10 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, 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); @@ -187552,6 +206729,8 @@ jtransform_request_workspace (j_decompress_ptr srcinfo, info->workspace_coef_arrays = coef_arrays; } +/* Transpose destination image parameters */ + LOCAL(void) transpose_critical_parameters (j_compress_ptr dstinfo) { @@ -187561,10 +206740,12 @@ transpose_critical_parameters (j_compress_ptr dstinfo) 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; @@ -187572,6 +206753,7 @@ transpose_critical_parameters (j_compress_ptr dstinfo) 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) { @@ -187586,12 +206768,18 @@ transpose_critical_parameters (j_compress_ptr dstinfo) } } +/* 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; @@ -187608,6 +206796,10 @@ 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; @@ -187618,27 +206810,50 @@ trim_bottom_edge (j_compress_ptr dstinfo) 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) @@ -187650,6 +206865,7 @@ jtransform_adjust_parameters (j_decompress_ptr srcinfo, break; case JXFORM_TRANSPOSE: transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ break; case JXFORM_TRANSVERSE: transpose_critical_parameters(dstinfo); @@ -187676,11 +206892,21 @@ jtransform_adjust_parameters (j_decompress_ptr srcinfo, 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, @@ -187718,15 +206944,21 @@ jtransform_execute_transformation (j_decompress_ptr srcinfo, #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); @@ -187734,12 +206966,24 @@ jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) #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 && @@ -187760,6 +207004,7 @@ jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, 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); @@ -188130,9 +207375,374 @@ namespace pnglibNamespace /*** 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)\ @@ -188222,20 +207885,44 @@ namespace pnglibNamespace #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 @@ -188248,10 +207935,51 @@ namespace pnglibNamespace # 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) @@ -188297,8 +208025,22 @@ namespace pnglibNamespace # 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 @@ -188324,10 +208066,18 @@ namespace pnglibNamespace # 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 */ @@ -188347,6 +208097,10 @@ namespace pnglibNamespace #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) @@ -188354,6 +208108,7 @@ namespace pnglibNamespace # endif #endif +/* enough people need this for various reasons to include it here */ #if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) # include #endif @@ -188363,6 +208118,9 @@ namespace pnglibNamespace #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 @@ -188370,11 +208128,15 @@ namespace pnglibNamespace # 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__ @@ -188393,14 +208155,30 @@ namespace pnglibNamespace # 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 @@ -188408,24 +208186,34 @@ namespace pnglibNamespace # 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 @@ -188436,22 +208224,70 @@ namespace pnglibNamespace # 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 @@ -188473,6 +208309,12 @@ namespace pnglibNamespace # 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 @@ -188496,6 +208338,7 @@ namespace pnglibNamespace # 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 @@ -188573,6 +208416,7 @@ namespace pnglibNamespace !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 */ @@ -188583,6 +208427,8 @@ namespace pnglibNamespace #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 @@ -188648,6 +208494,7 @@ namespace pnglibNamespace #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 @@ -188672,10 +208519,31 @@ namespace pnglibNamespace # 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 @@ -188688,6 +208556,7 @@ namespace pnglibNamespace # 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 @@ -188710,6 +208579,7 @@ namespace pnglibNamespace # 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) @@ -188717,6 +208587,7 @@ namespace pnglibNamespace #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) @@ -188725,6 +208596,9 @@ namespace pnglibNamespace #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 @@ -188732,6 +208606,42 @@ namespace pnglibNamespace # 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) @@ -188986,22 +208896,39 @@ namespace pnglibNamespace #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)) @@ -189010,12 +208937,24 @@ typedef unsigned char png_byte; # 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) @@ -189024,9 +208963,20 @@ typedef unsigned char png_byte; # 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 @@ -189035,16 +208985,21 @@ typedef unsigned char png_byte; # 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; @@ -189067,6 +209022,7 @@ typedef FILE * png_FILE_p; 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; @@ -189079,17 +209035,46 @@ typedef png_fixed_point FAR * FAR * png_fixed_point_pp; 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) @@ -189110,6 +209095,10 @@ typedef z_stream FAR * png_zstreamp; # 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) @@ -189126,6 +209115,13 @@ typedef z_stream FAR * png_zstreamp; # 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 @@ -189158,6 +209154,7 @@ typedef z_stream FAR * png_zstreamp; # 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 @@ -189220,6 +209217,10 @@ typedef z_stream FAR * png_zstreamp; # 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 @@ -189232,6 +209233,7 @@ typedef z_stream FAR * png_zstreamp; #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)) @@ -189257,6 +209259,11 @@ typedef z_stream FAR * png_zstreamp; # 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) \ @@ -189269,21 +209276,40 @@ typedef z_stream FAR * png_zstreamp; # 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) @@ -189298,10 +209324,17 @@ typedef z_stream FAR * png_zstreamp; #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 @@ -189336,24 +209369,38 @@ extern "C" { #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; @@ -189385,6 +209432,10 @@ typedef struct png_color_8_struct 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; @@ -189396,6 +209447,11 @@ typedef struct png_sPLT_entry_struct 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 */ @@ -189407,6 +209463,14 @@ 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: @@ -189430,6 +209494,8 @@ 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 @@ -189438,6 +209504,12 @@ typedef png_text FAR * FAR * png_textpp; #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 */ @@ -189451,12 +209523,18 @@ 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; @@ -189464,8 +209542,48 @@ 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) */ @@ -189475,64 +209593,131 @@ typedef struct png_info_struct 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; @@ -189546,6 +209731,17 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) #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 */ @@ -189555,28 +209751,40 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) 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 */ @@ -189589,6 +209797,8 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) #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 @@ -189612,65 +209822,86 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) 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 @@ -189688,6 +209919,10 @@ typedef png_info FAR * FAR * png_infopp; #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 */ @@ -189701,6 +209936,12 @@ typedef struct png_row_info_struct 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; @@ -189733,6 +209974,7 @@ typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp 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 */ @@ -189747,6 +209989,7 @@ typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); #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 @@ -189754,6 +209997,13 @@ typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); 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 @@ -189774,6 +210024,7 @@ struct png_struct_def 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) @@ -189915,6 +210166,7 @@ struct png_struct_def #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; @@ -189945,6 +210197,8 @@ struct png_struct_def 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 @@ -189959,16 +210213,20 @@ struct png_struct_def 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 @@ -189976,18 +210234,22 @@ struct png_struct_def #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) @@ -189998,20 +210260,26 @@ struct png_struct_def # 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 @@ -190019,29 +210287,54 @@ struct png_struct_def 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)); @@ -190056,8 +210349,10 @@ 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, @@ -190069,21 +210364,27 @@ extern PNG_EXPORT(png_structp,png_create_write_struct_2) 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,\ @@ -190093,12 +210394,14 @@ extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); 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 @@ -190109,16 +210412,20 @@ extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) #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 @@ -190127,19 +210434,23 @@ extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp 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 )); @@ -190168,10 +210479,13 @@ 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)); @@ -190179,32 +210493,39 @@ extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, #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, @@ -190217,16 +210538,19 @@ extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, #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)); @@ -190236,71 +210560,100 @@ extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, #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 */ @@ -190308,9 +210661,25 @@ extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, #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 @@ -190320,6 +210689,9 @@ extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, #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 @@ -190328,6 +210700,34 @@ extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, #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, @@ -190335,11 +210735,21 @@ extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, #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)); @@ -190355,21 +210765,47 @@ extern PNG_EXPORT(void,png_set_compression_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, @@ -190379,8 +210815,10 @@ 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 @@ -190402,6 +210840,7 @@ extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp 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 @@ -190414,17 +210853,25 @@ extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp #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 */ @@ -190435,28 +210882,37 @@ extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, #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 @@ -190490,63 +210946,95 @@ extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, #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 @@ -190554,11 +211042,13 @@ 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 @@ -190570,6 +211060,7 @@ 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)); @@ -190718,12 +211209,14 @@ extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, 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) @@ -190737,10 +211230,19 @@ extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, #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)); @@ -190796,6 +211298,15 @@ extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, #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, @@ -190810,10 +211321,14 @@ 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, @@ -190824,6 +211339,11 @@ extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, 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) @@ -190880,11 +211400,13 @@ 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 */ @@ -190915,24 +211437,31 @@ extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp #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)); @@ -190940,9 +211469,12 @@ extern PNG_EXPORT(void,png_set_mmx_thresholds) #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)); @@ -190950,6 +211482,7 @@ extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp #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)); @@ -190959,7 +211492,22 @@ 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) \ @@ -190987,6 +211535,14 @@ extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp #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))) @@ -190998,15 +211554,34 @@ 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 @@ -191015,6 +211590,9 @@ extern PNG_EXPORT(void,png_save_uint_16) #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 @@ -191025,6 +211603,7 @@ extern PNG_EXPORT(void,png_save_uint_16) #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 @@ -191034,6 +211613,7 @@ extern PNG_EXPORT(void,png_save_uint_16) #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 @@ -191048,17 +211628,26 @@ extern PNG_EXPORT(void,png_save_uint_16) #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 @@ -191082,6 +211671,14 @@ extern PNG_EXPORT(void,png_save_uint_16) #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) @@ -191092,25 +211689,38 @@ extern PNG_EXPORT(void,png_save_uint_16) #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'} @@ -191158,6 +211768,9 @@ 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, \ @@ -191173,6 +211786,9 @@ extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, #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, \ @@ -191185,8 +211801,10 @@ 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 @@ -191194,18 +211812,25 @@ PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_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)); @@ -191229,17 +211854,22 @@ PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, #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, @@ -191247,10 +211877,16 @@ PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, 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)); @@ -191258,8 +211894,14 @@ PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, 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, @@ -191313,6 +211955,7 @@ PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, 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) @@ -191396,40 +212039,58 @@ PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, #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)); @@ -191547,6 +212208,11 @@ 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, @@ -191645,6 +212311,7 @@ PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, 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)); @@ -191708,7 +212375,7 @@ PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, #if defined(PNG_ASSEMBLER_CODE_SUPPORTED) #if defined(PNG_MMX_CODE_SUPPORTED) - /* PRIVATE */ +/* png.c */ /* PRIVATE */ PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); #endif #endif @@ -191735,6 +212402,8 @@ 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 @@ -191742,25 +212411,43 @@ png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); #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; @@ -191784,23 +212471,41 @@ 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) @@ -191813,6 +212518,14 @@ png_set_sig_bytes(png_structp png_ptr, int num_bytes) 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) { @@ -191832,6 +212545,10 @@ png_sig_cmp(png_bytep sig, png_size_t start, png_size_t 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) { @@ -191841,6 +212558,7 @@ png_check_sig(png_bytep sig, int num) #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 @@ -191883,6 +212601,7 @@ png_zalloc(voidpf png_ptr, uInt items, uInt size) return ((voidpf)ptr); } +/* function to free memory for zlib */ #ifdef PNG_1_0_X void PNGAPI #else @@ -191893,12 +212612,20 @@ 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) { @@ -191920,6 +212647,12 @@ png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) 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) { @@ -191939,6 +212672,11 @@ png_create_info_struct(png_structp png_ptr) 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) { @@ -191963,11 +212701,16 @@ png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) } } +/* 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 @@ -191988,6 +212731,7 @@ png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) *ptr_ptr = info_ptr; } + /* set everything to 0 */ png_memset(info_ptr, 0, png_sizeof (png_info)); } @@ -192018,6 +212762,7 @@ png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, 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 @@ -192045,6 +212790,7 @@ if (mask & PNG_FREE_TEXT) #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 @@ -192061,6 +212807,7 @@ if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) #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 @@ -192078,6 +212825,7 @@ if (mask & PNG_FREE_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 @@ -192104,6 +212852,7 @@ if (mask & PNG_FREE_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 @@ -192119,6 +212868,7 @@ if (mask & PNG_FREE_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 @@ -192190,6 +212940,7 @@ if (mask & PNG_FREE_UNKN) #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 @@ -192205,6 +212956,7 @@ if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) } #endif +/* free any PLTE entry that was internally allocated */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) #else @@ -192221,6 +212973,7 @@ if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) } #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 @@ -192250,6 +213003,10 @@ if (mask & PNG_FREE_ROWS) #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) { @@ -192270,6 +213027,10 @@ png_info_destroy(png_structp png_ptr, png_infop info_ptr) } #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) { @@ -192279,6 +213040,12 @@ png_get_io_ptr(png_structp png_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) { @@ -192289,6 +213056,9 @@ png_init_io(png_structp png_ptr, png_FILE_p 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) { @@ -192347,9 +213117,18 @@ png_get_copyright(png_structp png_ptr) 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); } @@ -192357,6 +213136,7 @@ png_get_libpng_ver(png_structp png_ptr) 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); } @@ -192364,6 +213144,7 @@ png_get_header_ver(png_structp png_ptr) 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 @@ -192377,6 +213158,7 @@ png_get_header_version(png_structp png_ptr) 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) @@ -192389,6 +213171,7 @@ png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) } #endif +/* This function, added to libpng-1.0.6g, is untested. */ int PNGAPI png_reset_zstream(png_structp png_ptr) { @@ -192397,17 +213180,21 @@ png_reset_zstream(png_structp png_ptr) } #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 */ @@ -192415,6 +213202,7 @@ png_mmx_support(void) #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) @@ -192430,6 +213218,20 @@ png_convert_size(size_t size) /*** 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) @@ -192442,6 +213244,11 @@ 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) @@ -192485,6 +213292,8 @@ png_error(png_structp png_ptr, png_const_charp error_message) 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 @@ -192494,11 +213303,18 @@ 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) { @@ -192525,7 +213341,17 @@ png_warning(png_structp png_ptr, png_const_charp warning_message) } #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 */ @@ -192592,6 +213418,11 @@ png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) } #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) { @@ -192643,6 +213474,11 @@ png_default_error(png_structp png_ptr, png_const_charp error_message) } #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) { @@ -192677,6 +213513,11 @@ png_default_warning(png_structp png_ptr, png_const_charp warning_message) } #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) @@ -192688,6 +213529,10 @@ png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, 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) { @@ -192712,6 +213557,15 @@ png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) /*** 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) @@ -192746,6 +213600,7 @@ png_get_rows(png_structp png_ptr, png_infop info_ptr) #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) { @@ -193037,6 +213892,8 @@ png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, #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 @@ -193196,6 +214053,8 @@ png_get_iCCP(png_structp png_ptr, png_infop info_ptr, 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); @@ -193259,6 +214118,7 @@ png_get_IHDR(png_structp png_ptr, png_infop info_ptr, 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) @@ -193534,42 +214394,54 @@ png_get_compression_buffer_size(png_structp png_ptr) #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) { @@ -193587,12 +214459,31 @@ png_get_user_height_max (png_structp png_ptr) /*** 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) { @@ -193600,6 +214491,7 @@ png_create_struct(int type) 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) { @@ -193630,6 +214522,7 @@ png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) return (struct_ptr); } +/* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct(png_voidp struct_ptr) { @@ -193637,6 +214530,7 @@ png_destroy_struct(png_voidp struct_ptr) 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) @@ -193658,6 +214552,26 @@ png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, } } +/* 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) { @@ -193700,6 +214614,7 @@ png_malloc_default(png_structp png_ptr, png_uint_32 size) { 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)) { @@ -193814,6 +214729,9 @@ png_malloc_default(png_structp png_ptr, png_uint_32 size) 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) { @@ -193866,6 +214784,9 @@ png_free_default(png_structp png_ptr, png_voidp 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) { @@ -193873,6 +214794,9 @@ png_create_struct(int type) 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) { @@ -193915,6 +214839,7 @@ png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) return (struct_ptr); } +/* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct(png_voidp struct_ptr) { @@ -193922,6 +214847,7 @@ png_destroy_struct(png_voidp struct_ptr) 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) @@ -193951,6 +214877,12 @@ png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, } } +/* 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) { @@ -193990,6 +214922,7 @@ png_malloc_default(png_structp png_ptr, png_uint_32 size) } #endif + /* Check for overflow */ #if defined(__TURBOC__) && !defined(__FLAT__) if (size != (unsigned long)size) ret = NULL; @@ -194017,6 +214950,8 @@ png_malloc_default(png_structp png_ptr, png_uint_32 size) 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) { @@ -194055,6 +214990,11 @@ png_free_default(png_structp png_ptr, png_voidp ptr) #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) { @@ -194098,6 +215038,9 @@ png_memset_check (png_structp png_ptr, png_voidp s1, int value, } #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) @@ -194109,6 +215052,10 @@ png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr } } +/* 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) { @@ -194121,10 +215068,23 @@ png_get_mem_ptr(png_structp png_ptr) /*** 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) @@ -194135,6 +215095,7 @@ png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, 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, @@ -194162,6 +215123,7 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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; @@ -194204,6 +215166,11 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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')) @@ -194230,6 +215197,7 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, } } + /* 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); @@ -194252,6 +215220,9 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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(); @@ -194265,10 +215236,14 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_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); } @@ -194276,6 +215251,7 @@ 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 || @@ -194350,6 +215326,7 @@ png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, 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 @@ -194360,17 +215337,21 @@ png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, 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); @@ -194394,11 +215375,20 @@ png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, } #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, @@ -194490,6 +215480,9 @@ png_read_info(png_structp png_ptr, png_infop info_ptr) 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; @@ -194605,6 +215598,7 @@ png_read_info(png_structp png_ptr, png_infop info_ptr) } #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) { @@ -194619,6 +215613,11 @@ png_read_update_info(png_structp png_ptr, png_infop 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) { @@ -194647,6 +215646,7 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) 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."); @@ -194678,6 +215678,7 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) } #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) @@ -194820,6 +215821,7 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) 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 @@ -194828,10 +215830,15 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) 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) @@ -194857,6 +215864,29 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) #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, @@ -194896,6 +215926,18 @@ png_read_rows(png_structp png_ptr, png_bytepp row, #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) { @@ -194931,6 +215973,10 @@ png_read_image(png_structp png_ptr, png_bytepp image) #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) { @@ -195028,6 +216074,9 @@ png_read_end(png_structp png_ptr, png_infop info_ptr) #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); @@ -195108,6 +216157,7 @@ png_read_end(png_structp png_ptr, png_infop info_ptr) } #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) @@ -195177,6 +216227,7 @@ png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, } } +/* 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) { @@ -195292,6 +216343,9 @@ png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr #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 @@ -195336,35 +216390,58 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, 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) || @@ -195372,12 +216449,21 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, 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)) { @@ -195389,22 +216475,36 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, #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 @@ -195425,6 +216525,7 @@ png_read_png(png_structp png_ptr, png_infop 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 */ @@ -195438,10 +216539,20 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, /*** 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 @@ -195465,6 +216576,9 @@ png_process_data(png_structp png_ptr, png_infop 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) { @@ -195520,6 +216634,12 @@ png_process_some_data(png_structp png_ptr, png_infop info_ptr) } } +/* 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) { @@ -195612,6 +216732,12 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) 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]; @@ -195688,6 +216814,10 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) } 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 && @@ -196104,6 +217234,7 @@ png_push_read_IDAT(png_structp png_ptr) 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"); } @@ -196125,6 +217256,7 @@ png_push_read_IDAT(png_structp png_ptr) 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"); } @@ -196237,9 +217369,14 @@ png_push_process_row(png_structp png_ptr) 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) @@ -196392,15 +217529,24 @@ 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++; @@ -196514,7 +217660,7 @@ png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) key = png_ptr->current_text; for (text = key; *text; text++) - ; + /* empty loop */ ; if (text < key + png_ptr->current_text_size) text++; @@ -196553,6 +217699,10 @@ png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 } #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"); @@ -196604,8 +217754,9 @@ png_push_read_zTXt(png_structp png_ptr, png_infop info_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; @@ -196801,7 +217952,7 @@ png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) key = png_ptr->current_text; for (lang = key; *lang; lang++) - ; + /* empty loop */ ; if (lang < key + png_ptr->current_text_size - 3) lang++; @@ -196810,14 +217961,14 @@ png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) 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) @@ -196844,6 +217995,10 @@ png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) } #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) @@ -196885,6 +218040,7 @@ png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 #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); @@ -196971,10 +218127,31 @@ png_get_progressive_ptr(png_structp png_ptr) /*** 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) { @@ -196986,6 +218163,10 @@ png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) } #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) @@ -196993,6 +218174,9 @@ 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; @@ -197005,6 +218189,10 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t 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) @@ -197017,6 +218205,7 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) 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) @@ -197059,6 +218248,19 @@ png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) #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) @@ -197075,6 +218277,7 @@ png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, 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; @@ -197093,14 +218296,30 @@ png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, /*** 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) { @@ -197151,6 +218370,7 @@ png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) #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, @@ -197174,6 +218394,7 @@ png_set_background(png_structp png_ptr, #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) { @@ -197194,6 +218415,14 @@ png_set_strip_alpha(png_structp png_ptr) #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 { @@ -197227,15 +218456,25 @@ png_set_dither(png_structp png_ptr, png_colorp palette, { 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 */ @@ -197259,10 +218498,13 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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) @@ -197278,8 +218520,11 @@ png_set_dither(png_structp png_ptr, png_colorp palette, { 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; @@ -197291,17 +218536,20 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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++) @@ -197316,6 +218564,7 @@ png_set_dither(png_structp png_ptr, png_colorp palette, min_k = k; } } + /* point to closest color */ png_ptr->dither_index[i] = (png_byte)min_k; } } @@ -197325,6 +218574,14 @@ png_set_dither(png_structp png_ptr, png_colorp palette, } 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; @@ -197333,11 +218590,13 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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; @@ -197348,9 +218607,18 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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) @@ -197506,11 +218774,13 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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); @@ -197519,6 +218789,7 @@ png_set_dither(png_structp png_ptr, png_colorp palette, 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; @@ -197539,6 +218810,15 @@ png_set_dither(png_structp png_ptr, png_colorp palette, #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) { @@ -197554,6 +218834,10 @@ png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_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) { @@ -197565,6 +218849,24 @@ png_set_expand(png_structp png_ptr) #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) { @@ -197578,6 +218880,7 @@ png_set_palette_to_rgb(png_structp png_ptr) } #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) { @@ -197591,6 +218894,8 @@ png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) #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) { @@ -197600,6 +218905,7 @@ png_set_gray_1_2_4_to_8(png_structp png_ptr) } #endif +/* Expand tRNS chunks to alpha channels. */ void PNGAPI png_set_tRNS_to_alpha(png_structp png_ptr) { @@ -197625,6 +218931,9 @@ png_set_gray_to_rgb(png_structp png_ptr) #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, @@ -197706,6 +219015,9 @@ png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr } #endif +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ void /* PRIVATE */ png_init_read_transformations(png_structp png_ptr) { @@ -197722,6 +219034,15 @@ png_init_read_transformations(png_structp png_ptr) #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)) { @@ -197742,6 +219063,7 @@ png_init_read_transformations(png_structp png_ptr) { 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: @@ -197800,6 +219122,8 @@ png_init_read_transformations(png_structp png_ptr) 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; ipalette; int num_palette = png_ptr->num_palette; @@ -197935,7 +219261,9 @@ png_init_read_transformations(png_structp png_ptr) } } } + /* 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; @@ -197967,6 +219295,7 @@ png_init_read_transformations(png_structp png_ptr) (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( @@ -197982,6 +219311,7 @@ png_init_read_transformations(png_structp png_ptr) } 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 @@ -197990,6 +219320,7 @@ png_init_read_transformations(png_structp png_ptr) } } else + /* transformation does not include PNG_BACKGROUND */ #endif /* PNG_READ_BACKGROUND_SUPPORTED */ if (color_type == PNG_COLOR_TYPE_PALETTE) { @@ -198010,6 +219341,7 @@ png_init_read_transformations(png_structp png_ptr) #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)) { @@ -198030,6 +219362,7 @@ png_init_read_transformations(png_structp png_ptr) } 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, @@ -198073,6 +219406,10 @@ png_init_read_transformations(png_structp png_ptr) #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) { @@ -198174,11 +219511,13 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr) 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; @@ -198208,6 +219547,10 @@ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) #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) { @@ -198227,6 +219570,9 @@ png_do_read_transformations(png_structp png_ptr) } #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 @@ -198279,7 +219625,39 @@ png_do_read_transformations(png_structp png_ptr) } #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); @@ -198356,6 +219734,7 @@ png_do_read_transformations(png_structp png_ptr) #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); @@ -198389,6 +219768,12 @@ png_do_read_transformations(png_structp png_ptr) (*(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) @@ -198406,6 +219791,12 @@ png_do_read_transformations(png_structp png_ptr) } #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) { @@ -198491,6 +219882,11 @@ png_do_unpack(png_row_infop row_info, png_bytep row) #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) { @@ -198596,6 +219992,7 @@ png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) #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) { @@ -198614,9 +220011,31 @@ png_do_chop(png_row_infop row_info, png_bytep row) 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 } @@ -198639,6 +220058,7 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) 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; @@ -198655,6 +220075,7 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save; } } + /* This converts from RRGGBBAA to AARRGGBB */ else { png_bytep sp = row + row_info->rowbytes; @@ -198679,6 +220100,7 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) } 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; @@ -198693,6 +220115,7 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save; } } + /* This converts from GGAA to AAGG */ else { png_bytep sp = row + row_info->rowbytes; @@ -198727,6 +220150,7 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) 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; @@ -198737,10 +220161,17 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) { *(--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; @@ -198752,6 +220183,15 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) *(--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; } @@ -198759,6 +220199,7 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) } 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; @@ -198771,6 +220212,7 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) *(--dp) = *(--sp); } } + /* This inverts the alpha channel in GGAA */ else { png_bytep sp = row + row_info->rowbytes; @@ -198781,6 +220223,10 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) { *(--dp) = (png_byte)(255 - *(--sp)); *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ sp-=2; dp=sp; } @@ -198791,6 +220237,7 @@ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) #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) @@ -198810,6 +220257,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, { 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; @@ -198824,6 +220272,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, 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; @@ -198840,6 +220289,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, } 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; @@ -198857,6 +220307,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, 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; @@ -198878,6 +220329,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, { 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; @@ -198894,6 +220346,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, 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; @@ -198912,6 +220365,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, } 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; @@ -198933,6 +220387,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, 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; @@ -198958,6 +220413,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, #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) { @@ -199040,6 +220496,25 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) #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) @@ -199274,6 +220749,11 @@ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) } #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) { @@ -199318,6 +220798,7 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) } } +/* 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, @@ -199500,6 +220981,10 @@ png_correct_palette(png_structp png_ptr, png_colorp palette, #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 @@ -199709,6 +221194,7 @@ png_do_background(png_row_infop row_info, png_bytep row, 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); } @@ -199798,6 +221284,7 @@ png_do_background(png_row_infop row_info, png_bytep row, 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); @@ -199864,6 +221351,7 @@ png_do_background(png_row_infop row_info, png_bytep row, } else if (a == 0) { + /* background is already in screen gamma */ *dp = (png_byte)background->gray; } else @@ -199930,6 +221418,7 @@ png_do_background(png_row_infop row_info, png_bytep row, else #endif { + /* background is already in screen gamma */ *dp = (png_byte)((background->gray >> 8) & 0xff); *(dp + 1) = (png_byte)(background->gray & 0xff); } @@ -200006,6 +221495,7 @@ png_do_background(png_row_infop row_info, png_bytep row, } 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; @@ -200086,6 +221576,7 @@ png_do_background(png_row_infop row_info, png_bytep row, } 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); @@ -200177,6 +221668,12 @@ png_do_background(png_row_infop row_info, png_bytep row, #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, @@ -200356,6 +221853,9 @@ png_do_gamma(png_row_infop row_info, png_bytep row, #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) @@ -200496,6 +221996,9 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, } } +/* 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) @@ -200728,6 +222231,13 @@ png_do_dither(png_row_infop row_info, png_bytep row, 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)) | @@ -200791,6 +222301,11 @@ png_do_dither(png_row_infop row_info, png_bytep row, 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) { @@ -200998,9 +222513,11 @@ png_build_gamma_table(png_structp png_ptr) } } #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) { @@ -201064,6 +222581,18 @@ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) /*** 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) @@ -201074,6 +222603,7 @@ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) #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; @@ -201106,6 +222636,7 @@ png_get_uint_31(png_structp png_ptr, png_bytep buf) 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) { @@ -201117,6 +222648,9 @@ png_get_uint_32(png_bytep buf) 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) { @@ -201128,6 +222662,7 @@ png_get_int_32(png_bytep buf) 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) { @@ -201138,6 +222673,7 @@ png_get_uint_16(png_bytep buf) } #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) { @@ -201146,6 +222682,10 @@ png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t 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) { @@ -201180,6 +222720,8 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip) 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) { @@ -201212,6 +222754,13 @@ png_crc_error(png_structp png_ptr) #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, @@ -201258,6 +222807,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, 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); @@ -201374,6 +222924,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type, } #endif +/* read and check the IDHR chunk */ void /* PRIVATE */ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { @@ -201387,6 +222938,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -201403,6 +222955,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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; @@ -201413,6 +222966,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #endif png_ptr->compression_type = (png_byte)compression_type; + /* find number of channels */ switch (png_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: @@ -201430,6 +222984,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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); @@ -201440,6 +222995,7 @@ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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) { @@ -201511,12 +223067,17 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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 @@ -201526,6 +223087,10 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) @@ -201538,6 +223103,7 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -201610,6 +223176,7 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 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) @@ -201635,6 +223202,7 @@ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) return; igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ if (igamma == 0) { png_warning(png_ptr, @@ -201689,6 +223257,7 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } 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)) @@ -201757,6 +223326,7 @@ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 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) @@ -201914,6 +223484,7 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 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)) @@ -201935,6 +223506,7 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) return; intent = buf[0]; + /* check for bad intent */ if (intent >= PNG_sRGB_INTENT_LAST) { png_warning(png_ptr, "Unknown sRGB intent"); @@ -201994,6 +223566,7 @@ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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; @@ -202014,6 +223587,7 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 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)) @@ -202045,10 +223619,12 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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); @@ -202056,6 +223632,7 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) return; } + /* compression_type should always be zero */ compression_type = *profile++; if (compression_type) { @@ -202077,6 +223654,7 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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) | @@ -202102,6 +223680,7 @@ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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; @@ -202146,9 +223725,10 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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); @@ -202160,6 +223740,7 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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); @@ -202226,6 +223807,7 @@ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #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); @@ -202244,6 +223826,9 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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)) @@ -202296,6 +223881,7 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { 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 || @@ -202381,6 +223967,10 @@ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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]; @@ -202558,6 +224148,7 @@ png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) { @@ -202607,10 +224198,12 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -202626,6 +224219,8 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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) || @@ -202641,7 +224236,7 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } 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 @@ -202653,14 +224248,16 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -202679,6 +224276,7 @@ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) { @@ -202751,7 +224349,7 @@ png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #endif for (ep = buffer; *ep; ep++) - ; + /* empty loop */ ; ep++; if (buffer + slength < ep) @@ -202859,6 +224457,7 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) { @@ -202904,7 +224503,7 @@ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) key[slength] = 0x00; for (text = key; *text; text++) - ; + /* empty loop to find end of key */ ; if (text != key + slength) text++; @@ -202937,6 +224536,7 @@ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) { @@ -202955,6 +224555,8 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -202980,8 +224582,9 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -203031,6 +224634,7 @@ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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) { @@ -203051,6 +224655,8 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -203076,9 +224682,13 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) 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"); @@ -203092,7 +224702,7 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } for (lang_key = lang; *lang_key; lang_key++) - ; + /* empty loop */ ; lang_key++; /* skip NUL separator */ if (lang_key >= chunkdata + slength) @@ -203103,7 +224713,7 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } for (text = lang_key; *text; text++) - ; + /* empty loop */ ; text++; /* skip NUL separator */ if (text >= chunkdata + slength) { @@ -203145,6 +224755,11 @@ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #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) { @@ -203196,6 +224811,7 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 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); @@ -203228,6 +224844,12 @@ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) #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 */ @@ -203241,6 +224863,17 @@ png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) } } +/* 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) { @@ -203442,6 +225075,10 @@ png_combine_row(png_structp png_ptr, png_bytep row, int mask) } #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) { @@ -203450,6 +225087,8 @@ png_do_read_interlace(png_structp png_ptr) 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 @@ -203762,6 +225401,15 @@ png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, 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); @@ -203780,13 +225428,18 @@ 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 @@ -203903,13 +225556,18 @@ 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 @@ -204046,7 +225704,11 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) } #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 @@ -204081,6 +225743,20 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) /*** 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) @@ -204226,6 +225902,7 @@ png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) 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"); @@ -204299,6 +225976,8 @@ png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) #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) @@ -204330,6 +226009,7 @@ png_set_IHDR(png_structp png_ptr, png_infop info_ptr, 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 @@ -204349,6 +226029,7 @@ png_set_IHDR(png_structp png_ptr, png_infop info_ptr, - 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"); @@ -204370,6 +226051,15 @@ png_set_IHDR(png_structp png_ptr, png_infop info_ptr, 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) @@ -204405,6 +226095,7 @@ png_set_IHDR(png_structp png_ptr, png_infop info_ptr, 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 */ @@ -204597,10 +226288,18 @@ png_set_PLTE(png_structp png_ptr, png_infop info_ptr, } } + /* + * 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 * @@ -204748,6 +226447,8 @@ png_set_iCCP(png_structp png_ptr, png_infop info_ptr, 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; @@ -204779,6 +226480,9 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, 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) @@ -204834,6 +226538,7 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, else #ifdef PNG_iTXt_SUPPORTED { + /* set iTXt data */ if (text_ptr[i].lang != NULL) lang_len = png_strlen(text_ptr[i].lang); else @@ -204948,9 +226653,15 @@ png_set_tRNS(png_structp png_ptr, png_infop info_ptr, 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) @@ -205010,9 +226721,11 @@ png_set_sPLT(png_structp png_ptr, 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) @@ -205078,6 +226791,7 @@ png_set_unknown_chunks(png_structp png_ptr, 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); } } @@ -205104,6 +226818,8 @@ png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, 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; @@ -205227,28 +226943,37 @@ png_set_invalid(png_structp png_ptr, png_infop info_ptr, int 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; @@ -205261,10 +226986,20 @@ png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, /*** 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) { @@ -205275,6 +227010,7 @@ png_set_bgr(png_structp png_ptr) #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) { @@ -205286,6 +227022,7 @@ png_set_swap(png_structp png_ptr) #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) { @@ -205300,6 +227037,7 @@ png_set_packing(png_structp png_ptr) #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) { @@ -205338,6 +227076,11 @@ png_set_interlace_handling(png_structp png_ptr) #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) { @@ -205350,11 +227093,19 @@ png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) 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; @@ -205362,6 +227113,7 @@ png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) } #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) { @@ -205405,10 +227157,14 @@ png_set_invert_mono(png_structp png_ptr) 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; @@ -205456,6 +227212,7 @@ png_do_invert(png_row_infop row_info, png_bytep row) #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) { @@ -205586,6 +227343,7 @@ static PNG_CONST png_byte fourbppswaptable[256] = { 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) { @@ -205617,6 +227375,7 @@ png_do_packswap(png_row_infop row_info, png_bytep row) #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) { @@ -205637,6 +227396,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) { if (row_info->bit_depth == 8) { + /* This converts from RGBX or RGBA to RGB */ if (flags & PNG_FLAG_FILLER_AFTER) { dp+=3; sp+=4; @@ -205648,6 +227408,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) sp++; } } + /* This converts from XRGB or ARGB to RGB */ else { for (i = 0; i < row_width; i++) @@ -205665,9 +227426,15 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) { 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++; @@ -205680,8 +227447,14 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) } 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++; @@ -205704,6 +227477,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) { 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++) @@ -205712,6 +227486,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) sp++; } } + /* This converts from XG or AG to G */ else { for (i = 0; i < row_width; i++) @@ -205727,6 +227502,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) { 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++) { @@ -205737,6 +227513,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) } else { + /* This converts from XXGG or AAGG to GG */ for (i = 0; i < row_width; i++) { sp += 2; @@ -205756,6 +227533,7 @@ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) #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) { @@ -205852,6 +227630,11 @@ png_set_user_transform_info(png_structp png_ptr, png_voidp } #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) { @@ -205867,10 +227650,32 @@ png_get_user_transform_ptr(png_structp png_ptr) /*** 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) { @@ -205881,6 +227686,10 @@ png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) } #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) @@ -205898,6 +227707,10 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t 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) @@ -205910,6 +227723,7 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) 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) @@ -205953,6 +227767,9 @@ png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) #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) @@ -205978,6 +227795,28 @@ png_default_flush(png_structp png_ptr) #endif #endif +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + 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) @@ -206005,6 +227844,7 @@ png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, #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; @@ -206047,10 +227887,29 @@ void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) /*** 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) { @@ -206067,6 +227926,7 @@ png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) 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, @@ -206075,6 +227935,8 @@ png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) #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) { @@ -206171,6 +228033,7 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) 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) { @@ -206235,13 +228098,16 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) 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, @@ -206251,28 +228117,34 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) #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; } } @@ -206302,6 +228174,11 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) #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) { @@ -206311,24 +228188,29 @@ png_write_end(png_structp png_ptr, png_infop info_ptr) 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, @@ -206338,28 +228220,33 @@ png_write_end(png_structp png_ptr, png_infop info_ptr) #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; } } @@ -206390,11 +228277,13 @@ png_write_end(png_structp png_ptr, png_infop info_ptr) 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) { @@ -206419,6 +228308,7 @@ png_convert_from_time_t(png_timep ptime, time_t ttime) #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) @@ -206428,6 +228318,7 @@ png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, 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, @@ -206451,6 +228342,7 @@ png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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; @@ -206487,6 +228379,11 @@ png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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')) @@ -206513,6 +228410,7 @@ png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, } } + /* 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); @@ -206526,6 +228424,9 @@ png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, #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(); @@ -206538,11 +228439,14 @@ png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, 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); } @@ -206550,6 +228454,7 @@ 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 || @@ -206624,6 +228529,7 @@ png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, 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 @@ -206634,20 +228540,24 @@ png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, *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); @@ -206658,6 +228568,11 @@ png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, #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) @@ -206670,12 +228585,16 @@ png_write_rows(png_structp png_ptr, png_bytepp row, 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) { @@ -206688,12 +228607,16 @@ png_write_image(png_structp png_ptr, png_bytepp image) 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); @@ -206701,6 +228624,7 @@ png_write_image(png_structp png_ptr, png_bytepp image) } } +/* called by user to write a row of image data */ void PNGAPI png_write_row(png_structp png_ptr, png_bytep row) { @@ -206709,12 +228633,15 @@ png_write_row(png_structp png_ptr, png_bytep row) 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."); @@ -206748,6 +228675,7 @@ png_write_row(png_structp png_ptr, png_bytep row) } #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) @@ -206805,6 +228733,7 @@ png_write_row(png_structp png_ptr, png_bytep row) } #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; @@ -206822,15 +228751,18 @@ png_write_row(png_structp png_ptr, png_bytep row) 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); @@ -206839,17 +228771,29 @@ png_write_row(png_structp png_ptr, png_bytep row) } #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) @@ -206857,6 +228801,7 @@ png_write_row(png_structp png_ptr, png_bytep row) } #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) { @@ -206866,6 +228811,7 @@ png_set_flush(png_structp png_ptr, int nrows) png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); } +/* flush the current output buffers now */ void PNGAPI png_write_flush(png_structp png_ptr) { @@ -206874,6 +228820,7 @@ png_write_flush(png_structp png_ptr) 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; @@ -206881,9 +228828,11 @@ png_write_flush(png_structp png_ptr) { 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) @@ -206894,6 +228843,7 @@ png_write_flush(png_structp png_ptr) 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; @@ -206902,8 +228852,10 @@ png_write_flush(png_structp png_ptr) } } 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; @@ -206914,6 +228866,7 @@ png_write_flush(png_structp 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) { @@ -206972,6 +228925,7 @@ png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) } } +/* Free any memory used in png_ptr struct (old method) */ void /* PRIVATE */ png_write_destroy(png_structp png_ptr) { @@ -206986,8 +228940,10 @@ png_write_destroy(png_structp png_ptr) #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); @@ -207009,6 +228965,7 @@ png_write_destroy(png_structp png_ptr) #endif #ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); #endif @@ -207033,6 +228990,7 @@ png_write_destroy(png_structp png_ptr) #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) { @@ -207070,6 +229028,15 @@ png_set_filter(png_structp png_ptr, int method, int filters) #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 @@ -207135,6 +229102,13 @@ png_set_filter(png_structp png_ptr, int method, int filters) 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, @@ -207173,6 +229147,7 @@ png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, 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; @@ -207210,6 +229185,9 @@ png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, } } + /* 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, @@ -207225,6 +229203,13 @@ png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, } } + /* 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) @@ -207283,6 +229268,7 @@ png_set_compression_window_bits(png_structp png_ptr, int window_bits) 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"); @@ -207334,56 +229320,76 @@ png_write_png(png_structp png_ptr, png_infop info_ptr, 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 */ @@ -207395,10 +229401,22 @@ png_write_png(png_structp png_ptr, png_infop info_ptr, /*** 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) { @@ -207413,6 +229431,12 @@ png_do_write_transformations(png_structp png_ptr) (*(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) @@ -207457,6 +229481,10 @@ png_do_write_transformations(png_structp png_ptr) } #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) { @@ -207576,6 +229604,13 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) #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) { @@ -207615,6 +229650,7 @@ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) channels++; } + /* with low row depths, could only be grayscale, so one channel */ if (row_info->bit_depth < 8) { png_bytep bp = row; @@ -207709,6 +229745,7 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) { 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; @@ -207723,6 +229760,7 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save; } } + /* This converts from AARRGGBB to RRGGBBAA */ else { png_bytep sp, dp; @@ -207747,6 +229785,7 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) } 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; @@ -207760,6 +229799,7 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save; } } + /* This converts from AAGG to GGAA */ else { png_bytep sp, dp; @@ -207793,6 +229833,7 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) { 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; @@ -207800,10 +229841,16 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) 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; @@ -207812,6 +229859,14 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) 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++)); @@ -207820,6 +229875,7 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) } 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; @@ -207832,6 +229888,7 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) *(dp++) = (png_byte)(255 - *(sp++)); } } + /* This inverts the alpha channel in GGAA */ else { png_bytep sp, dp; @@ -207840,6 +229897,10 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) 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++)); @@ -207851,6 +229912,7 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) #endif #if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ void /* PRIVATE */ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) { @@ -207914,10 +229976,23 @@ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) /*** 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) { @@ -207927,6 +230002,10 @@ png_save_uint_32(png_bytep buf, png_uint_32 i) 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) { @@ -207936,6 +230015,10 @@ png_save_int_32(png_bytep buf, png_int_32 i) 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) { @@ -207943,6 +230026,15 @@ png_save_uint_16(png_bytep buf, unsigned int i) 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) @@ -207953,6 +230045,10 @@ png_write_chunk(png_structp png_ptr, png_bytep chunk_name, 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) @@ -207961,17 +230057,26 @@ png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, 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) { @@ -207980,6 +230085,7 @@ png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) } } +/* Finish a chunk started with png_write_chunk_start(). */ void PNGAPI png_write_chunk_end(png_structp png_ptr) { @@ -207987,15 +230093,23 @@ png_write_chunk_end(png_structp png_ptr) 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) @@ -208003,6 +230117,12 @@ png_write_sig(png_structp png_ptr) } #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 { @@ -208013,6 +230133,7 @@ typedef struct 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, @@ -208026,6 +230147,7 @@ png_text_compress(png_structp png_ptr, 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; @@ -208044,23 +230166,44 @@ png_text_compress(png_structp png_ptr, #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; @@ -208085,25 +230228,32 @@ png_text_compress(png_structp png_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; @@ -208115,6 +230265,7 @@ png_text_compress(png_structp png_ptr, 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))); @@ -208128,18 +230279,21 @@ png_text_compress(png_structp png_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 @@ -208147,6 +230301,7 @@ png_text_compress(png_structp png_ptr, } } 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; @@ -208154,11 +230309,13 @@ png_text_compress(png_structp png_ptr, 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, @@ -208166,6 +230323,7 @@ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) 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], @@ -208176,15 +230334,21 @@ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) 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, @@ -208196,6 +230360,7 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 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: @@ -208244,6 +230409,15 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 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) && @@ -208269,6 +230443,7 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 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; @@ -208281,10 +230456,12 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 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; @@ -208293,8 +230470,10 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 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; @@ -208327,11 +230506,17 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, 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) { @@ -208380,6 +230565,7 @@ png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) 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++) { @@ -208393,6 +230579,7 @@ png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) 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) { @@ -208401,12 +230588,17 @@ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) #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) { @@ -208439,6 +230631,7 @@ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) png_ptr->mode |= PNG_HAVE_IDAT; } +/* write an IEND chunk */ void /* PRIVATE */ png_write_IEND(png_structp png_ptr) { @@ -208452,6 +230645,7 @@ png_write_IEND(png_structp png_ptr) } #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) @@ -208463,6 +230657,7 @@ png_write_gAMA(png_structp png_ptr, double file_gamma) 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); @@ -208478,6 +230673,7 @@ png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) 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); } @@ -208485,6 +230681,7 @@ png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) #endif #if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ void /* PRIVATE */ png_write_sRGB(png_structp png_ptr, int srgb_intent) { @@ -208503,6 +230700,7 @@ png_write_sRGB(png_structp png_ptr, int srgb_intent) #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) @@ -208561,6 +230759,7 @@ png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, 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; @@ -208575,6 +230774,7 @@ png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, #endif #if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ void /* PRIVATE */ png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) { @@ -208599,11 +230799,13 @@ png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) 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++) { @@ -208655,6 +230857,7 @@ png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) #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) { @@ -208665,6 +230868,7 @@ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) 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; @@ -208709,6 +230913,7 @@ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) #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, @@ -208722,6 +230927,7 @@ png_write_cHRM(png_structp png_ptr, double white_x, double white_y, 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) { @@ -208782,6 +230988,7 @@ png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, 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"); @@ -208823,6 +231030,7 @@ png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, #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) @@ -208840,10 +231048,12 @@ png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, 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, @@ -208855,6 +231065,7 @@ png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, } 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); @@ -208874,6 +231085,7 @@ png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, #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) { @@ -208926,6 +231138,7 @@ png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) #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) { @@ -208956,6 +231169,16 @@ png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) #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) { @@ -208982,6 +231205,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) 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 || @@ -209005,6 +231229,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) } *dp = '\0'; + /* Remove any trailing white space. */ kp = *new_key + key_len - 1; if (*kp == ' ') { @@ -209017,6 +231242,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) } } + /* Remove any leading white space. */ kp = *new_key; if (*kp == ' ') { @@ -209031,6 +231257,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) 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) @@ -209072,6 +231299,7 @@ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) #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) @@ -209094,7 +231322,14 @@ png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, 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); @@ -209105,6 +231340,7 @@ png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, #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) @@ -209140,23 +231376,30 @@ png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, 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) @@ -209198,9 +231441,13 @@ png_write_iTXt(png_structp png_ptr, int compression, png_charp key, 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 */ @@ -209209,13 +231456,21 @@ png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + 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); @@ -209232,6 +231487,7 @@ png_write_iTXt(png_structp png_ptr, int compression, png_charp key, #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) @@ -209253,6 +231509,7 @@ png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, } #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) @@ -209279,6 +231536,8 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, 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); @@ -209310,6 +231569,7 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, #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) @@ -209324,6 +231584,7 @@ png_write_sCAL(png_structp png_ptr, int unit, double width, double height) buf[0] = (char)unit; #if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ { wchar_t wc_buf[32]; size_t wc_len; @@ -209382,6 +231643,7 @@ png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, #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, @@ -209405,6 +231667,9 @@ png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, #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) { @@ -209433,18 +231698,24 @@ png_write_tIME(png_structp png_ptr, png_timep mod_time) } #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 @@ -209455,10 +231726,12 @@ png_write_start_row(png_structp png_ptr) 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, @@ -209466,8 +231739,10 @@ png_write_start_row(png_structp png_ptr) 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); @@ -209495,6 +231770,7 @@ png_write_start_row(png_structp png_ptr) } #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)) @@ -209520,18 +231796,24 @@ png_write_start_row(png_structp png_ptr) 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 @@ -209539,12 +231821,15 @@ png_write_finish_row(png_structp png_ptr) 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; @@ -209554,6 +231839,7 @@ png_write_finish_row(png_structp png_ptr) } else { + /* loop until we find a non-zero width or height pass */ do { png_ptr->pass++; @@ -209573,6 +231859,7 @@ png_write_finish_row(png_structp png_ptr) } + /* reset the row above the image for the next pass */ if (png_ptr->pass < 7) { if (png_ptr->prev_row != NULL) @@ -209584,11 +231871,16 @@ png_write_finish_row(png_structp png_ptr) } #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); @@ -209605,6 +231897,7 @@ png_write_finish_row(png_structp png_ptr) } } 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 - @@ -209616,23 +231909,35 @@ png_write_finish_row(png_structp png_ptr) } #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: @@ -209743,19 +232048,27 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) 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]) / @@ -209766,6 +232079,10 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) } #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) @@ -209784,6 +232101,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) #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; @@ -209793,6 +232111,30 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) 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) { @@ -209815,6 +232157,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) 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) @@ -209826,6 +232169,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } + /* 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]) >> @@ -209840,7 +232187,9 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) 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; @@ -209865,6 +232214,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) 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; @@ -209951,6 +232304,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } + /* up filter */ if (filter_to_do == PNG_FILTER_UP) { png_bytep rp, dp, pp; @@ -210052,6 +232406,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } + /* avg filter */ if (filter_to_do == PNG_FILTER_AVG) { png_bytep rp, dp, pp, lp; @@ -210163,6 +232518,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } + /* Paeth filter */ if (filter_to_do == PNG_FILTER_PAETH) { png_bytep rp, dp, pp, cp, lp; @@ -210326,11 +232682,13 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } #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; @@ -210344,19 +232702,24 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) #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) @@ -210365,14 +232728,18 @@ png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) 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; @@ -210382,6 +232749,7 @@ png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) 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) @@ -210667,6 +233035,13 @@ END_JUCE_NAMESPACE #if JUCE_WINDOWS /*** Start of inlined file: juce_win32_NativeCode.cpp ***/ +/* + This file wraps together all the win32-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + #if JUCE_WINDOWS BEGIN_JUCE_NAMESPACE @@ -215420,6 +237795,24 @@ bool Desktop::isScreenSaverEnabled() throw() return screenSaverDefeater == 0; } +/* (The code below is the "correct" way to disable the screen saver, but it + completely fails on winXP when the saver is password-protected...) + +static bool juce_screenSaverEnabled = true; + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + juce_screenSaverEnabled = isEnabled; + SetThreadExecutionState (isEnabled ? ES_CONTINUOUS + : (ES_DISPLAY_REQUIRED | ES_CONTINUOUS)); +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return juce_screenSaverEnabled; +} +*/ + void juce_setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /*allowMenusAndBars*/) { if (enableOrDisable) @@ -217493,6 +239886,19 @@ bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle 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; @@ -226805,10 +249211,21 @@ END_JUCE_NAMESPACE #if JUCE_LINUX /*** Start of inlined file: juce_linux_NativeCode.cpp ***/ +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + #if JUCE_LINUX BEGIN_JUCE_NAMESPACE +/* 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) +*/ #if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES) #define SUPPORT_AFFINITIES 1 #endif @@ -226819,6 +249236,12 @@ BEGIN_JUCE_NAMESPACE /*** Start of inlined file: juce_posix_SharedCode.h ***/ +/* + 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; @@ -227950,6 +250373,8 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd return false; } +/** A HTTP input stream that uses sockets. + */ class JUCE_HTTPSocketStream { public: @@ -228553,6 +250978,11 @@ void PlatformUtilities::fpuReset() // compiled on its own). #if JUCE_INCLUDED_FILE +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + void JUCE_API juce_threadEntryPoint (void*); void* threadEntryProc (void* value) @@ -228592,6 +251022,20 @@ Thread::ThreadID Thread::getCurrentThreadId() return (ThreadID) 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 bool juce_setThreadPriority (void* handle, int priority) @@ -228635,6 +251079,14 @@ void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) 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(); @@ -228948,6 +251400,15 @@ const String SystemClipboard::getTextFromClipboard() { initSelectionAtoms(); + /* 1) try to read from the "CLIPBOARD" selection first (the "high + level" clipboard that is supposed to be filled by ctrl-C + etc). When a clipboard manager is running, the content of this + selection is preserved even when the original selection owner + exits. + + 2) and then try to read from "PRIMARY" selection (the "legacy" selection + filled by good old x11 apps such as xterm) + */ String content; Atom selection = XA_PRIMARY; Window selectionOwner = None; @@ -229282,6 +251743,7 @@ bool juce_postMessageToSystemQueue (void* message) void MessageManager::broadcastMessage (const String& value) throw() { + /* TODO */ } void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, @@ -233609,6 +256071,16 @@ public: return false; } + /* +#if 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; @@ -235523,6 +257995,10 @@ void FileChooser::showPlatformDialog (Array& results, // compiled on its own). #if JUCE_INCLUDED_FILE && JUCE_WEB_BROWSER +/* + Sorry.. This class isn't implemented on Linux! +*/ + WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_) : browser (0), blankPageShown (false), @@ -235627,6 +258103,13 @@ END_JUCE_NAMESPACE #if JUCE_MAC || JUCE_IPHONE /*** Start of inlined file: juce_mac_NativeCode.mm ***/ +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + #if JUCE_MAC || JUCE_IPHONE BEGIN_JUCE_NAMESPACE @@ -235639,6 +258122,19 @@ BEGIN_JUCE_NAMESPACE /*** Start of inlined file: juce_mac_ObjCSuffix.h ***/ +/** This suffix is used for naming all Obj-C classes that are used inside juce. + + Because of the flat naming structure used by Obj-C, you can get horrible situations where + two DLLs are loaded into a host, each of which uses classes with the same names, and these get + cross-linked so that when you make a call to a class that you thought was private, it ends up + actually calling into a similarly named class in the other module's address space. + + By changing this macro to a unique value, you ensure that all the obj-C classes in your app + have unique names, and should avoid this problem. + + If you're using the amalgamated version, you can just set this macro to something unique before + you include juce_amalgamated.cpp. +*/ #ifndef JUCE_ObjCExtraSuffix #define JUCE_ObjCExtraSuffix 3 #endif @@ -236641,6 +259137,11 @@ int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOut // compiled on its own). #if JUCE_INCLUDED_FILE +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + void JUCE_API juce_threadEntryPoint (void*); void* threadEntryProc (void* userData) @@ -236735,6 +259236,12 @@ void Process::setPriority (ProcessPriority p) /*** Start of inlined file: juce_posix_SharedCode.h ***/ +/* + 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; @@ -237310,6 +259817,11 @@ void InterProcessLock::exit() // compiled on its own). #if JUCE_INCLUDED_FILE +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + bool File::copyInternal (const File& dest) const { const ScopedAutoReleasePool pool; @@ -242016,6 +264528,20 @@ private: void interruptionListener (UInt32 inInterruption) { + /*if (inInterruption == kAudioSessionBeginInterruption) + { + isRunning = false; + AudioOutputUnitStop (audioUnit); + + if (juce_iPhoneShowModalAlert ("Audio Interrupted", + "This could have been interrupted by another application or by unplugging a headset", + @"Resume", + @"Cancel")) + { + isRunning = true; + propertyChanged (0, 0, 0); + } + }*/ if (inInterruption == kAudioSessionEndInterruption) { @@ -244205,6 +266731,16 @@ public: int getCurrentRenderingEngine() throw(); void setCurrentRenderingEngine (int index) throw(); + /* When you use multiple DLLs which share similarly-named obj-c classes - like + for example having more than one juce plugin loaded into a host, then when a + method is called, the actual code that runs might actually be in a different module + than the one you expect... So any calls to library functions or statics that are + made inside obj-c methods will probably end up getting executed in a different DLL's + memory space. Not a great thing to happen - this obviously leads to bizarre crashes. + + To work around this insanity, I'm only allowing obj-c methods to make calls to + virtual methods of an object that's known to live inside the right module's space. + */ virtual void redirectMouseDown (NSEvent* ev); virtual void redirectMouseUp (NSEvent* ev); virtual void redirectMouseDrag (NSEvent* ev); @@ -248259,6 +270795,16 @@ void AudioCDReader::ejectDisk() // compiled on its own). #if JUCE_INCLUDED_FILE +/* When you use multiple DLLs which share similarly-named obj-c classes - like + for example having more than one juce plugin loaded into a host, then when a + method is called, the actual code that runs might actually be in a different module + than the one you expect... So any calls to library functions or statics that are + made inside obj-c methods will probably end up getting executed in a different DLL's + memory space. Not a great thing to happen - this obviously leads to bizarre crashes. + + To work around this insanity, I'm only allowing obj-c methods to make calls to + virtual methods of an object that's known to live inside the right module's space. +*/ class AppDelegateRedirector { public: diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 23609b6c6a..b9e6429832 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -23,6 +23,18 @@ ============================================================================== */ +/* + ============================================================================== + + 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 __JUCE_AMALGAMATED_TEMPLATE_JUCEHEADER__ #define __JUCE_AMALGAMATED_TEMPLATE_JUCEHEADER__ @@ -33,6 +45,11 @@ #ifndef __JUCE_JUCEHEADER__ #define __JUCE_JUCEHEADER__ +/* + This is the main JUCE header file that applications need to include. + +*/ + #define JUCE_PUBLIC_INCLUDES 1 // (this includes things that need defining outside of the JUCE namespace) @@ -41,10 +58,22 @@ #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 52 -#define JUCE_BUILDNUMBER 1 +#define JUCE_BUILDNUMBER 2 +/** 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) + JUCE_BUILDNUMBER) @@ -52,6 +81,18 @@ #ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ #define __JUCE_TARGETPLATFORM_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_WINDOWS, 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 +*/ + #if (defined (_WIN32) || defined (_WIN64)) #define JUCE_WIN32 1 #define JUCE_WINDOWS 1 @@ -86,6 +127,7 @@ #define JUCE_MINGW 1 #endif + /** If defined, this indicates that the processor is little-endian. */ #define JUCE_LITTLE_ENDIAN 1 #define JUCE_INTEL 1 @@ -182,38 +224,75 @@ #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 +/** JUCE_FORCE_DEBUG: 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 to force + it to be true or false. +*/ #ifndef JUCE_FORCE_DEBUG //#define JUCE_FORCE_DEBUG 0 #endif +/** JUCE_LOG_ASSERTIONS: 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 0 #endif +/** JUCE_ASIO: Enables ASIO audio devices (MS Windows only). + Turning this on means that you'll need to have the Steinberg ASIO SDK installed + on your Windows build machine. + + See the comments in the ASIOAudioIODevice class's header file for more + info about this. +*/ #ifndef JUCE_ASIO #define JUCE_ASIO 0 #endif +/** JUCE_WASAPI: Enables WASAPI audio devices (Windows Vista and above). +*/ #ifndef JUCE_WASAPI #define JUCE_WASAPI 0 #endif +/** JUCE_DIRECTSOUND: Enables DirectSound audio (MS Windows only). +*/ #ifndef JUCE_DIRECTSOUND #define JUCE_DIRECTSOUND 1 #endif +/** JUCE_ALSA: Enables ALSA audio devices (Linux only). */ #ifndef JUCE_ALSA #define JUCE_ALSA 1 #endif +/** JUCE_JACK: Enables JACK audio devices (Linux only). */ #ifndef JUCE_JACK #define JUCE_JACK 0 #endif +/** JUCE_QUICKTIME: Enables the QuickTimeMovieComponent class (Mac and Windows). + If you're building on Windows, you'll need to have the Apple QuickTime SDK + installed, and its header files will need to be on your include path. +*/ #if ! (defined (JUCE_QUICKTIME) || JUCE_LINUX || JUCE_IPHONE || (JUCE_WINDOWS && ! JUCE_MSVC)) #define JUCE_QUICKTIME 0 #endif @@ -222,70 +301,132 @@ #undef JUCE_QUICKTIME #endif +/** JUCE_OPENGL: Enables the OpenGLComponent class (available on all platforms). + If you're not using OpenGL, you might want to turn this off to reduce your binary's size. +*/ #ifndef JUCE_OPENGL #define JUCE_OPENGL 1 #endif +/** JUCE_USE_FLAC: Enables the FLAC audio codec classes (available on all platforms). + If your app doesn't need to read FLAC files, you might want to disable this to + reduce the size of your codebase and build time. +*/ #ifndef JUCE_USE_FLAC #define JUCE_USE_FLAC 1 #endif +/** JUCE_USE_OGGVORBIS: Enables the Ogg-Vorbis audio codec classes (available on all platforms). + If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to + reduce the size of your codebase and build time. +*/ #ifndef JUCE_USE_OGGVORBIS #define JUCE_USE_OGGVORBIS 1 #endif +/** JUCE_USE_CDBURNER: Enables the audio CD reader code (Mac and Windows only). + Unless you're using CD-burning, you should probably turn this flag off to + reduce code size. +*/ #if (! defined (JUCE_USE_CDBURNER)) && ! (JUCE_WINDOWS && ! JUCE_MSVC) #define JUCE_USE_CDBURNER 0 #endif +/** JUCE_USE_CDREADER: Enables the audio CD reader code (Mac and Windows only). + Unless you're using CD-reading, you should probably turn this flag off to + reduce code size. +*/ #ifndef JUCE_USE_CDREADER #define JUCE_USE_CDREADER 0 #endif +/** JUCE_USE_CAMERA: Enables web-cam support using the CameraDevice class (Mac and Windows). +*/ #if (JUCE_QUICKTIME || JUCE_WINDOWS) && ! defined (JUCE_USE_CAMERA) #define JUCE_USE_CAMERA 0 #endif +/** JUCE_ENABLE_REPAINT_DEBUGGING: If this option is turned on, each area of the screen that + gets repainted will flash in a random colour, so that you can check exactly how much and how + often your components are being drawn. +*/ #ifndef JUCE_ENABLE_REPAINT_DEBUGGING #define JUCE_ENABLE_REPAINT_DEBUGGING 0 #endif +/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only). + Unless you specifically want to disable this, it's best to leave this option turned on. +*/ #ifndef JUCE_USE_XINERAMA #define JUCE_USE_XINERAMA 1 #endif +/** JUCE_USE_XSHM: Enables X shared memory for faster rendering on Linux. This is best left + turned on unless you have a good reason to disable it. +*/ #ifndef JUCE_USE_XSHM #define JUCE_USE_XSHM 1 #endif +/** JUCE_USE_XRENDER: Uses XRender to allow semi-transparent windowing on Linux. +*/ #ifndef JUCE_USE_XRENDER #define JUCE_USE_XRENDER 0 #endif +/** JUCE_USE_XCURSOR: Uses XCursor to allow ARGB cursor on Linux. This is best left turned on + unless you have a good reason to disable it. +*/ #ifndef JUCE_USE_XCURSOR #define JUCE_USE_XCURSOR 1 #endif +/** JUCE_PLUGINHOST_VST: Enables the VST audio plugin hosting classes. This requires the + Steinberg VST SDK to be installed on your machine, and should be left turned off unless + you're building a plugin hosting app. + + @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU +*/ #ifndef JUCE_PLUGINHOST_VST #define JUCE_PLUGINHOST_VST 0 #endif +/** JUCE_PLUGINHOST_AU: Enables the AudioUnit plugin hosting classes. This is Mac-only, + of course, and should only be enabled if you're building a plugin hosting app. + + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST +*/ #ifndef JUCE_PLUGINHOST_AU #define JUCE_PLUGINHOST_AU 0 #endif +/** JUCE_ONLY_BUILD_CORE_LIBRARY: Enabling this will avoid including any UI classes in the build. + This should be enabled if you're writing a console application. +*/ #ifndef JUCE_ONLY_BUILD_CORE_LIBRARY #define JUCE_ONLY_BUILD_CORE_LIBRARY 0 #endif +/** JUCE_WEB_BROWSER: This lets you disable the WebBrowserComponent class (Mac and Windows). + If you're not using any embedded web-pages, turning this off may reduce your code size. +*/ #ifndef JUCE_WEB_BROWSER #define JUCE_WEB_BROWSER 1 #endif +/** JUCE_SUPPORT_CARBON: Enabling this allows the Mac code to use old Carbon library functions. + + Carbon isn't required for a normal app, but may be needed by specialised classes like + plugin-hosts, which support older APIs. +*/ #ifndef JUCE_SUPPORT_CARBON #define JUCE_SUPPORT_CARBON 1 #endif +/* JUCE_INCLUDE_ZLIB_CODE: Can be used to disable Juce's embedded 3rd-party zlib code. + You might need to tweak this if you're linking to an external zlib library in your app, + but for normal apps, this option should be left alone. +*/ #ifndef JUCE_INCLUDE_ZLIB_CODE #define JUCE_INCLUDE_ZLIB_CODE 1 #endif @@ -306,10 +447,17 @@ #define JUCE_INCLUDE_JPEGLIB_CODE 1 #endif +/** JUCE_CHECK_MEMORY_LEAKS: Enables a memory-leak check when an app terminates. + (Currently, this only affects Windows builds in debug mode). +*/ #ifndef JUCE_CHECK_MEMORY_LEAKS #define JUCE_CHECK_MEMORY_LEAKS 1 #endif +/** JUCE_CATCH_UNHANDLED_EXCEPTIONS: Turn on juce's internal catching of exceptions + that are thrown by the message dispatch loop. With it enabled, any unhandled exceptions + are passed to the JUCEApplication::unhandledException() callback for logging. +*/ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 #endif @@ -348,6 +496,9 @@ #ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ #define __JUCE_PLATFORMDEFS_JUCEHEADER__ +/* This file defines miscellaneous macros for debugging, assertions, etc. +*/ + #ifdef JUCE_FORCE_DEBUG #undef JUCE_DEBUG @@ -356,6 +507,7 @@ #endif #endif +/** This macro defines the C calling convention used as the standard for Juce calls. */ #if JUCE_MSVC #define JUCE_CALLTYPE __stdcall #else @@ -377,6 +529,12 @@ // 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); // Assertions.. @@ -386,11 +544,20 @@ #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 @@ -453,6 +620,9 @@ #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) \ { \ @@ -478,6 +648,12 @@ // 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 + */ #ifndef JUCE_DEBUG #define forcedinline __forceinline #else @@ -485,6 +661,12 @@ #endif #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 @@ -567,9 +749,11 @@ #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... @@ -586,17 +770,34 @@ extern bool JUCE_PUBLIC_FUNCTION juce_isRunningUnderDebugger(); #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 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. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_malloc(numBytes) _malloc_dbg (numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling calloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_calloc(numBytes) _calloc_dbg (1, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling realloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_realloc(location, numBytes) _realloc_dbg (location, numBytes, _NORMAL_BLOCK, __FILE__, __LINE__) + /** This should be used instead of calling free directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_free(location) _free_dbg (location, _NORMAL_BLOCK) #else @@ -610,16 +811,32 @@ extern bool JUCE_PUBLIC_FUNCTION juce_isRunningUnderDebugger(); 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. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_malloc(numBytes) JUCE_NAMESPACE::juce_DebugMalloc (numBytes, __FILE__, __LINE__) + /** This should be used instead of calling calloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_calloc(numBytes) JUCE_NAMESPACE::juce_DebugCalloc (numBytes, __FILE__, __LINE__) + /** This should be used instead of calling realloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_realloc(location, numBytes) JUCE_NAMESPACE::juce_DebugRealloc (location, numBytes, __FILE__, __LINE__) + /** This should be used instead of calling free directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_free(location) JUCE_NAMESPACE::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, void* p) { return p; } \ @@ -638,12 +855,24 @@ extern bool JUCE_PUBLIC_FUNCTION juce_isRunningUnderDebugger(); 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. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_malloc(numBytes) JUCE_NAMESPACE::juce_Malloc (numBytes) + /** This should be used instead of calling calloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_calloc(numBytes) JUCE_NAMESPACE::juce_Calloc (numBytes) + /** This should be used instead of calling realloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_realloc(location, numBytes) JUCE_NAMESPACE::juce_Realloc (location, numBytes) + /** This should be used instead of calling free directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_free(location) JUCE_NAMESPACE::juce_Free (location) #define juce_UseDebuggingNewOperator \ @@ -656,35 +885,70 @@ extern bool JUCE_PUBLIC_FUNCTION juce_isRunningUnderDebugger(); // Mac, Linux and Win32 (release) versions.. + /** This should be used instead of calling malloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_malloc(numBytes) malloc (numBytes) + /** This should be used instead of calling calloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_calloc(numBytes) calloc (1, numBytes) + /** This should be used instead of calling realloc directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #define juce_realloc(location, numBytes) realloc (location, numBytes) + /** This should be used instead of calling free directly. + Only use direct memory allocation if there's really no way to use a HeapBlock object instead! + */ #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-independent 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 #if JUCE_MINGW + /** This allocator is not defined in mingw gcc. */ #define alloca __builtin_alloca #endif +/** Clears a block of memory. */ inline void zeromem (void* memory, size_t numBytes) { memset (memory, 0, numBytes); } +/** Clears a reference to a local structure. */ template inline void zerostruct (Type& structure) { memset (&structure, 0, sizeof (structure)); } +/** A handy function that calls delete on a pointer if it's non-zero, and then sets + the pointer to null. +*/ template inline void deleteAndZero (Type& pointer) { delete pointer; pointer = 0; } @@ -696,58 +960,113 @@ inline void deleteAndZero (Type& pointer) { delete pointer; pointer = 0; } #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. */ template inline Type jmax (const Type a, const Type b) { return (a < b) ? b : a; } +/** Returns the larger of three values. */ template inline Type jmax (const Type a, const Type b, const Type c) { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } +/** Returns the larger of four values. */ template inline Type jmax (const Type a, const Type b, const Type c, const Type d) { return jmax (a, jmax (b, c, d)); } +/** Returns the smaller of two values. */ template inline Type jmin (const Type a, const Type b) { return (b < a) ? b : a; } +/** Returns the smaller of three values. */ template inline Type jmin (const Type a, const Type b, const Type c) { return (b < a) ? ((c < b) ? c : b) : ((c < a) ? c : a); } +/** Returns the smaller of four values. */ template inline Type jmin (const Type a, const Type b, const Type c, const Type d) { 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, @@ -760,6 +1079,8 @@ inline Type jlimit (const Type lowerLimit, : valueToConstrain); } +/** Handy function to swap two values over. +*/ template inline void swapVariables (Type& variable1, Type& variable2) { @@ -768,11 +1089,22 @@ inline void swapVariables (Type& variable1, Type& variable2) variable2 = tempVal; } +/** Handy function 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 +*/ template inline int numElementsInArray (Type& array) { return static_cast (sizeof (array) / sizeof (array[0])); } // Some useful maths functions that aren't always present with all compilers and build settings. +/** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ inline double juce_hypot (double a, double b) { #if JUCE_WINDOWS @@ -782,6 +1114,8 @@ inline double juce_hypot (double a, double b) #endif } +/** Using juce_hypot and juce_hypotf is easier than dealing with all the different + versions of these functions of various platforms and compilers. */ inline float juce_hypotf (float a, float b) { #if JUCE_WINDOWS @@ -791,15 +1125,27 @@ inline float juce_hypotf (float a, float b) #endif } +/** 64-bit abs function. */ inline int64 abs64 (const int64 n) { 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 between platforms, so this is a + platform-independent function for it. +*/ template inline bool juce_isfinite (FloatingPointType value) { @@ -810,6 +1156,16 @@ inline bool juce_isfinite (FloatingPointType value) #endif } +/** 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. +*/ template inline int roundToInt (const FloatType value) throw() { @@ -823,23 +1179,63 @@ inline int roundToInt (const FloatType value) throw() #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 roundToIntAccurate (const double value) throw() { return roundToInt (value + 1.5e-8); } +/** 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() { return roundToInt (value); } +/** 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() { return roundToInt (value); } +/** This namespace contains a few template classes for helping work out class type variations. +*/ namespace TypeHelpers { + /** The ParameterType struct is used to find the best type to use when passing some kind + of object as a parameter. + + Of course, this is only likely to be useful in certain esoteric template situations. + + Because "typename TypeHelpers::ParameterType::type" is a bit of a mouthful, there's + a PARAMETER_TYPE(SomeClass) macro that you can use to get the same effect. + + E.g. "myFunction (PARAMETER_TYPE (int), PARAMETER_TYPE (MyObject))" + would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as + pass-by-value, but passing objects as a const reference, to avoid copying. + */ #if defined (_MSC_VER) && _MSC_VER <= 1400 #define PARAMETER_TYPE(a) a #else @@ -872,44 +1268,65 @@ namespace TypeHelpers #ifndef __JUCE_BYTEORDER_JUCEHEADER__ #define __JUCE_BYTEORDER_JUCEHEADER__ +/** Contains static methods for converting the byte order between different + endiannesses. +*/ class JUCE_API ByteOrder { public: + /** Swaps the upper and lower bytes of a 16-bit integer. */ static uint16 swap (uint16 value); + /** Reverses the order of the 4 bytes in a 32-bit integer. */ static uint32 swap (uint32 value); + /** Reverses the order of the 8 bytes in a 64-bit integer. */ static uint64 swap (uint64 value); + /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ static uint16 swapIfBigEndian (uint16 value); + /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ static uint32 swapIfBigEndian (uint32 value); + /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ static uint64 swapIfBigEndian (uint64 value); + /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ static uint16 swapIfLittleEndian (uint16 value); + /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ static uint32 swapIfLittleEndian (uint32 value); + /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ static uint64 swapIfLittleEndian (uint64 value); + /** Turns 4 bytes into a little-endian integer. */ static uint32 littleEndianInt (const void* bytes); + /** Turns 2 bytes into a little-endian integer. */ static uint16 littleEndianShort (const void* bytes); + /** Turns 4 bytes into a big-endian integer. */ static uint32 bigEndianInt (const void* bytes); + /** Turns 2 bytes into a big-endian integer. */ static uint16 bigEndianShort (const void* bytes); + /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ static int littleEndian24Bit (const char* bytes); + /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ static int bigEndian24Bit (const char* bytes); + /** Copies a 24-bit number to 3 little-endian bytes. */ static void littleEndian24BitToChars (int value, char* destBytes); + /** Copies a 24-bit number to 3 big-endian bytes. */ static void bigEndian24BitToChars (int value, char* destBytes); + /** Returns true if the current CPU is big-endian. */ static bool isBigEndian(); private: @@ -1015,10 +1432,30 @@ typedef juce_wchar tchar; #if ! JUCE_DONT_DEFINE_MACROS +/** The 'T' macro allows a literal string to be compiled as unicode. + + If you write your string literals in the form T("xyz"), it will be compiled as L"xyz" + or "xyz", depending on which representation is best for the String class to work with. + + 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: @@ -1101,6 +1538,9 @@ public: 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 juce_wchar digit) throw(); }; @@ -1109,130 +1549,394 @@ public: class OutputStream; +/** + 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* text); + /** 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* text, size_t maxChars); + /** Creates a string from a zero-terminated unicode text string. */ String (const juce_wchar* unicodeText); + /** 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* unicodeText, size_t maxChars); + /** Creates a string from a single character. */ static const String charToString (juce_wchar character); + /** Destructor. */ ~String() throw(); //========================juce_wchar====================================================== + /** 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. */ String& operator= (const String& other) throw(); + /** Appends another string at the end of this one. */ String& operator+= (const juce_wchar* textToAppend); + /** Appends another string at the end of this one. */ String& operator+= (const String& stringToAppend); + /** Appends a character at the end of this string. */ String& operator+= (char characterToAppend); + /** Appends a character at the end of this string. */ String& operator+= (juce_wchar characterToAppend); + /** Appends a decimal number at the end of this string. */ String& operator+= (int numberToAppend); + /** Appends a decimal number at the end of this string. */ String& operator+= (unsigned int numberToAppend); + /** 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 juce_wchar* textToAppend, int maxCharsToTake); // Comparison methods.. + /** Returns true if the string contains no characters. + + Note that there's also an isNotEmpty() method to help write readable code. + + @see containsNonWhitespaceChars() + */ inline bool isEmpty() const throw() { return 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. + + @see containsNonWhitespaceChars() + */ inline bool isNotEmpty() const throw() { return text[0] != 0; } + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const String& other) const throw(); + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const juce_wchar* other) const throw(); + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const char* 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 String& 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 char* 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 juce_wchar* 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 String& 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 String& other) const throw(); + /** Tests whether the string begins with another string. + Uses a case-sensitive comparison. + */ bool startsWith (const String& text) const throw(); + /** Tests whether the string begins with a particular character. + Uses a case-sensitive comparison. + */ bool startsWithChar (juce_wchar character) const throw(); + /** Tests whether the string begins with another string. + Uses a case-insensitive comparison. + */ bool startsWithIgnoreCase (const String& text) const throw(); + /** Tests whether the string ends with another string. + Uses a case-sensitive comparison. + */ bool endsWith (const String& text) const throw(); + /** Tests whether the string ends with a particular character. + Uses a case-sensitive comparison. + */ bool endsWithChar (juce_wchar character) const throw(); + /** Tests whether the string ends with another string. + Uses a case-insensitive comparison. + */ bool endsWithIgnoreCase (const String& text) const throw(); + /** Tests whether the string contains another substring. + Uses a case-sensitive comparison. + */ bool contains (const String& text) const throw(); + /** Tests whether the string contains a particular character. + Uses a case-sensitive comparison. + */ bool containsChar (juce_wchar character) const throw(); + /** Tests whether the string contains another substring. + Uses a case-insensitive comparison. + */ bool containsIgnoreCase (const String& 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 String& 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 String& 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 String& 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 String& 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 String& 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 String& charactersItMightContain) const throw(); + /** Returns true if this string contains any non-whitespace characters. + + This will return false if the string contains only whitespace characters, or + if it's empty. + + It is equivalent to calling "myString.trim().isNotEmpty()". + */ bool containsNonWhitespaceChars() 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 String& wildcard, 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 (juce_wchar 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 (int startIndex, juce_wchar 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 String& charactersToLookFor, int startIndex = 0, 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 String& 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 (int startIndex, const String& 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 String& 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 (int startIndex, const String& 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 (juce_wchar 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 String& 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 String& 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 String& charactersToLookFor, bool ignoreCase = false) const throw(); @@ -1244,153 +1948,567 @@ public: */ inline const juce_wchar& operator[] (int index) const throw() { jassert (((unsigned int) index) <= (unsigned int) length()); return 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. + */ juce_wchar& operator[] (int index); + /** Returns the final character of the string. + + If the string is empty this will return 0. + */ juce_wchar 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, getLastCharacters, upToFirstOccurrenceOf + */ const String substring (int startIndex, int endIndex) const; + /** 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, getLastCharacters, fromFirstOccurrenceOf, upToFirstOccurrenceOf, fromLastOccurrenceOf + */ const String substring (int startIndex) const; + /** 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 (int numberToDrop) const; + /** Returns a number of characters from the end of the string. + + This returns the last numCharacters characters from the end of the string. If the + string is shorter than numCharacters, the whole string is returned. + + @see substring, dropLastCharacters, getLastCharacter + */ const String getLastCharacters (int numCharacters) const; + /** 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 String& substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const; + /** 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 String& substringToFind, bool includeSubStringInResult, bool ignoreCase) const; + /** 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 String& substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const; + /** 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. + If the substring isn't found, this will return an empty string. + + @see upToFirstOccurrenceOf, fromFirstOccurrenceOf + */ const String upToLastOccurrenceOf (const String& substringToFind, bool includeSubStringInResult, bool ignoreCase) const; + /** Returns a copy of this string with any whitespace characters removed from the start and end. */ const String trim() const; + /** Returns a copy of this string with any whitespace characters removed from the start. */ const String trimStart() const; + /** Returns a copy of this string with any whitespace characters removed from the end. */ const String trimEnd() const; + /** Returns a copy of this string, having removed a specified set of characters from its start. + Characters are removed from the start of the string until it finds one that is not in the + specified set, and then it stops. + @param charactersToTrim the set of characters to remove. + @see trim, trimStart, trimCharactersAtEnd + */ const String trimCharactersAtStart (const String& charactersToTrim) const; + /** Returns a copy of this string, having removed a specified set of characters from its end. + Characters are removed from the end of the string until it finds one that is not in the + specified set, and then it stops. + @param charactersToTrim the set of characters to remove. + @see trim, trimEnd, trimCharactersAtStart + */ const String trimCharactersAtEnd (const String& charactersToTrim) const; + /** Returns an upper-case version of this string. */ const String toUpperCase() const; + /** Returns an lower-case version of this string. */ const String toLowerCase() const; + /** 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 String& stringToInsert) const; + /** 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 String& stringToReplace, const String& stringToInsertInstead, bool ignoreCase = false) const; + /** Returns a string with all occurrences of a character replaced with a different one. */ const String replaceCharacter (juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const; + /** 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. replaceCharacters ("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 String& charactersToInsertInstead) const; + /** 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 String& charactersToRetain) const; + /** 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 String& charactersToRemove) const; + /** 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 String& permittedCharacters) const; + /** 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 String& charactersToStopAt) const; + /** 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; + /** 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; + /** 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 (juce_wchar quoteCharacter = '"') const; + /** 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 String& stringToRepeat, int numberOfTimesToRepeat); + /** Returns a copy of this string with the specified character repeatedly added to its + beginning until the total length is at least the minimum length specified. + */ const String paddedLeft (juce_wchar padCharacter, int minimumLength) const; + /** Returns a copy of this string with the specified character repeatedly added to its + end until the total length is at least the minimum length specified. + */ const String paddedRight (juce_wchar padCharacter, int minimumLength) const; + /** 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* data, int size); + /** Creates a String from a printf-style parameter list. + + I don't like this method. I don't use it myself, and I recommend avoiding it and + using the operator<< methods or pretty much anything else instead. It's only provided + here because of the popular unrest that was stirred-up when I tried to remove it... + + If you're really determined to use it, at least make sure that you never, ever, + pass any String objects to it as parameters. + */ static const String formatted (const juce_wchar* formatString, ... ); // Numeric conversions.. + /** Creates a string containing this signed 32-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (int decimalInteger); + /** Creates a string containing this unsigned 32-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (unsigned int decimalInteger); + /** Creates a string containing this signed 16-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (short decimalInteger); + /** Creates a string containing this unsigned 16-bit integer as a decimal number. + + @see getIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (unsigned short decimalInteger); + /** Creates a string containing this signed 64-bit integer as a decimal number. + + @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (int64 largeIntegerValue); + /** Creates a string containing this unsigned 64-bit integer as a decimal number. + + @see getLargeIntValue, getFloatValue, getDoubleValue, toHexString + */ explicit String (uint64 largeIntegerValue); + /** 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 (float floatValue, int numberOfDecimalPlaces = 0); + /** 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 (double doubleValue, int numberOfDecimalPlaces = 0); + /** Reads the value of the string as a decimal number (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(); + /** Reads the value of the string as a decimal number (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 (int number); + /** Creates a string representing this 64-bit value in hexadecimal. */ static const String toHexString (int64 number); + /** Creates a string representing this 16-bit value in hexadecimal. */ static const String toHexString (short number); + /** 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, int size, int groupSize = 1); + /** 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 become invalid whenever + any string methods (even some const ones!) are called. + */ inline operator const juce_wchar*() const throw() { return 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 become invalid whenever + any string methods (even some const ones!) are called. + */ inline operator juce_wchar*() throw() { return text; } + /** 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. + + @see getNumBytesAsUTF8, fromUTF8, copyToUTF8, toCString + */ const char* toUTF8() const; + /** 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 char* utf8buffer, int bufferSizeBytes = -1); + /** Returns the number of bytes required to represent this string as UTF8. + The number returned does NOT include the trailing zero. + @see toUTF8, copyToUTF8 + */ int getNumBytesAsUTF8() 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). + @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the + string won't fit, it'll put in as many as it can while + still allowing for a terminating null char at the end, and + will return the number of bytes that were actually used. + */ int copyToUTF8 (char* destBuffer, int maxBufferSizeBytes) const throw(); + /** Returns a version of this string using the default 8-bit multi-byte 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. + + @see getNumBytesAsCString, copyToCString, toUTF8 + */ const char* toCString() const; + /** Returns the number of bytes + */ int getNumBytesAsCString() const throw(); + /** Copies the string to a buffer. + + @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). + @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the + string won't fit, it'll put in as many as it can while + still allowing for a terminating null char at the end, and + will return the number of bytes that were actually used. + */ int copyToCString (char* destBuffer, int maxBufferSizeBytes) 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 copyToUnicode (juce_wchar* destBuffer, int maxCharsToCopy) const 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 (size_t numCharsNeeded); + /** Swaps the contents of this string with another one. + This is a very fast operation, as no allocation or copying needs to be done. + */ void swapWith (String& other) throw(); + /** A helper class to improve performance when concatenating many large strings + together. + + Because appending one string to another involves measuring the length of + both strings, repeatedly doing this for many long strings will become + an exponentially slow operation. This class uses some internal state to + avoid that, so that each append operation only needs to measure the length + of the appended string. + */ class JUCE_API Concatenator { public: @@ -1421,70 +2539,141 @@ private: void appendInternal (const juce_wchar* text, int numExtraChars); }; +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (const char* string1, const String& string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (const juce_wchar* string1, const String& string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (char string1, const String& string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (juce_wchar string1, const String& string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (String string1, const String& string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (String string1, const char* string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (String string1, const juce_wchar* string2); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (String string1, char characterToAppend); +/** Concatenates two strings. */ const String JUCE_CALLTYPE operator+ (String string1, juce_wchar characterToAppend); +/** Appends a character at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, char characterToAppend); +/** Appends a character at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, juce_wchar characterToAppend); +/** Appends a string to the end of the first one. */ String& JUCE_CALLTYPE operator<< (String& string1, const char* string2); +/** Appends a string to the end of the first one. */ String& JUCE_CALLTYPE operator<< (String& string1, const juce_wchar* string2); +/** Appends a string to the end of the first one. */ String& JUCE_CALLTYPE operator<< (String& string1, const String& string2); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, short number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, int number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, unsigned int number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, long number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, unsigned long number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, float number); +/** Appends a decimal number at the end of a string. */ String& JUCE_CALLTYPE operator<< (String& string1, double number); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator== (const String& string1, const String& string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator== (const String& string1, const char* string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator== (const String& string1, const juce_wchar* string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator!= (const String& string1, const String& string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator!= (const String& string1, const char* string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator!= (const String& string1, const juce_wchar* string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator< (const String& string1, const String& string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator>= (const String& string1, const String& string2) throw(); +/** Case-sensitive comparison of two strings. */ bool JUCE_CALLTYPE operator<= (const String& string1, const String& string2) throw(); +/** This streaming override allows you to pass a juce String directly into std output streams. + This is very handy for writing strings to std::cout, std::cerr, etc. +*/ template std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostream & stream, const String& stringToWrite) { return stream << stringToWrite.toUTF8(); } +/** Writes a string to an OutputStream as UTF8. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text); #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(); protected: Logger(); + /** This is overloaded by subclasses to implement custom logging behaviour. + + @see setCurrentLogger + */ virtual void logMessage (const String& message) = 0; }; @@ -1528,59 +2717,171 @@ BEGIN_JUCE_NAMESPACE #ifndef __JUCE_HEAPBLOCK_JUCEHEADER__ #define __JUCE_HEAPBLOCK_JUCEHEADER__ +/** + Very simple container class to hold a pointer to some data on the heap. + + When you need to allocate some heap storage for something, always try to use + this class instead of allocating the memory directly using malloc/free. + + A HeapBlock object can be treated in pretty much exactly the same way + as an char*, but as long as you allocate it on the stack or as a class member, + it's almost impossible for it to leak memory. + + It also makes your code much more concise and readable than doing the same thing + using direct allocations, + + E.g. instead of this: + @code + int* temp = (int*) juce_malloc (1024 * sizeof (int)); + memcpy (temp, xyz, 1024 * sizeof (int)); + juce_free (temp); + temp = (int*) juce_calloc (2048 * sizeof (int)); + temp[0] = 1234; + memcpy (foobar, temp, 2048 * sizeof (int)); + juce_free (temp); + @endcode + + ..you could just write this: + @code + HeapBlock temp (1024); + memcpy (temp, xyz, 1024 * sizeof (int)); + temp.calloc (2048); + temp[0] = 1234; + memcpy (foobar, temp, 2048 * sizeof (int)); + @endcode + + The class is extremely lightweight, containing only a pointer to the + data, and exposes malloc/realloc/calloc/free methods that do the same jobs + as their less object-oriented counterparts. Despite adding safety, you probably + won't sacrifice any performance by using this in place of normal pointers. + + @see Array, OwnedArray, MemoryBlock +*/ template class HeapBlock { public: + /** Creates a HeapBlock which is initially just a null pointer. + + After creation, you can resize the array using the malloc(), calloc(), + or realloc() methods. + */ HeapBlock() throw() : data (0) { } + /** Creates a HeapBlock containing a number of elements. + + The contents of the block are undefined, as it will have been created by a + malloc call. + + If you want an array of zero values, you can use the calloc() method instead. + */ explicit HeapBlock (const size_t numElements) : data (static_cast (::juce_malloc (numElements * sizeof (ElementType)))) { } + /** Destructor. + + This will free the data, if any has been allocated. + */ ~HeapBlock() { ::juce_free (data); } + /** Returns a raw pointer to the allocated data. + This may be a null pointer if the data hasn't yet been allocated, or if it has been + freed by calling the free() method. + */ inline operator ElementType*() const throw() { return data; } + /** Returns a raw pointer to the allocated data. + This may be a null pointer if the data hasn't yet been allocated, or if it has been + freed by calling the free() method. + */ inline ElementType* getData() const throw() { return data; } + /** Returns a void pointer to the allocated data. + This may be a null pointer if the data hasn't yet been allocated, or if it has been + freed by calling the free() method. + */ inline operator void*() const throw() { return static_cast (data); } + /** Lets you use indirect calls to the first element in the array. + Obviously this will cause problems if the array hasn't been initialised, because it'll + be referencing a null pointer. + */ inline ElementType* operator->() const throw() { return data; } + /** Returns a reference to one of the data elements. + Obviously there's no bounds-checking here, as this object is just a dumb pointer and + has no idea of the size it currently has allocated. + */ template inline ElementType& operator[] (IndexType index) const throw() { return data [index]; } + /** Returns a pointer to a data element at an offset from the start of the array. + This is the same as doing pointer arithmetic on the raw pointer itself. + */ template inline ElementType* operator+ (IndexType index) const throw() { return data + index; } + /** Returns a reference to the raw data pointer. + Beware that the pointer returned here will become invalid as soon as you call + any of the allocator methods on this object! + */ inline ElementType* const* operator&() const throw() { return static_cast (&data); } + /** Returns a reference to the raw data pointer. + Beware that the pointer returned here will become invalid as soon as you call + any of the allocator methods on this object! + */ inline ElementType** operator&() throw() { return static_cast (&data); } + /** Compares the pointer with another pointer. + This can be handy for checking whether this is a null pointer. + */ inline bool operator== (const ElementType* const otherPointer) const throw() { return otherPointer == data; } + /** Compares the pointer with another pointer. + This can be handy for checking whether this is a null pointer. + */ inline bool operator!= (const ElementType* const otherPointer) const throw() { return otherPointer != data; } + /** Allocates a specified amount of memory. + + This uses the normal malloc to allocate an amount of memory for this object. + Any previously allocated memory will be freed by this method. + + The number of bytes allocated will be (newNumElements * elementSize). Normally + you wouldn't need to specify the second parameter, but it can be handy if you need + to allocate a size in bytes rather than in terms of the number of elements. + + The data that is allocated will be freed when this object is deleted, or when you + call free() or any of the allocation methods. + */ void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { ::juce_free (data); data = static_cast (::juce_malloc (newNumElements * elementSize)); } + /** Allocates a specified amount of memory and clears it. + This does the same job as the malloc() method, but clears the memory that it allocates. + */ void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { ::juce_free (data); data = static_cast (::juce_calloc (newNumElements * elementSize)); } + /** Allocates a specified amount of memory and optionally clears it. + This does the same job as either malloc() or calloc(), depending on the + initialiseToZero parameter. + */ void allocate (const size_t newNumElements, const bool initialiseToZero) { ::juce_free (data); @@ -1591,6 +2892,11 @@ public: data = static_cast (::juce_malloc (newNumElements * sizeof (ElementType))); } + /** Re-allocates a specified amount of memory. + + The semantics of this method are the same as malloc() and calloc(), but it + uses realloc() to keep as much of the existing data as possible. + */ void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { if (data == 0) @@ -1599,12 +2905,18 @@ public: data = static_cast (::juce_realloc (data, newNumElements * elementSize)); } + /** Frees any currently-allocated data. + This will free the data and reset this object to be a null pointer. + */ void free() { ::juce_free (data); data = 0; } + /** Swaps this object's data with the data of another HeapBlock. + The two objects simply exchange their data pointers. + */ void swapWith (HeapBlock & other) throw() { swapVariables (data, other.data); @@ -1621,20 +2933,40 @@ private: #endif // __JUCE_HEAPBLOCK_JUCEHEADER__ /*** End of inlined file: juce_HeapBlock.h ***/ +/** + 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. + + It inherits from a critical section class to allow the arrays to use + the "empty base class optimisation" pattern to reduce their footprint. + + @see Array, OwnedArray, ReferenceCountedArray +*/ template class ArrayAllocationBase : public TypeOfCriticalSectionToUse { public: + /** Creates an empty array. */ ArrayAllocationBase() throw() : numAllocated (0) { } + /** Destructor. */ ~ArrayAllocationBase() { } + /** 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) { if (numAllocated != numElements) @@ -1648,18 +2980,30 @@ public: } } + /** 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 (const int minNumElements) { if (minNumElements > numAllocated) setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7); } + /** Minimises the amount of storage allocated so that it's no more than + the given number of elements. + */ void shrinkToNoMoreThan (const int maxNumElements) { if (maxNumElements < numAllocated) setAllocatedSize (maxNumElements); } + /** Swap the contents of two objects. */ void swapWith (ArrayAllocationBase & other) throw() { elements.swapWith (other.elements); @@ -1682,6 +3026,33 @@ private: #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, @@ -1813,6 +3184,29 @@ static void sortArray (ElementComparator& comparator, } } +/** + 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, @@ -1857,6 +3251,20 @@ static int findInsertIndexInSortedArray (ElementComparator& comparator, return firstElement; } +/** + A simple ElementComparator class that can be used to sort an array of + objects that support the '<' operator. + + This will work for primitive types and objects that implement operator<(). + + Example: @code + Array myArray; + DefaultElementComparator sorter; + myArray.sort (sorter); + @endcode + + @see ElementComparator +*/ template class DefaultElementComparator { @@ -1881,22 +3289,64 @@ public: class JUCE_API ScopedLock; class JUCE_API ScopedUnlock; +/** + 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(); + /** Provides the type of scoped lock to use with this type of critical section object. */ typedef ScopedLock ScopedLockType; + /** Provides the type of scoped unlocker to use with this type of critical section object. */ typedef ScopedUnlock ScopedUnlockType; juce_UseDebuggingNewOperator @@ -1920,6 +3370,14 @@ private: CriticalSection& operator= (const CriticalSection&); }; +/** + A class that can be used in place of a real CriticalSection object. + + This is currently used by some templated classes, and should get + optimised out by the compiler. + + @see Array, OwnedArray, ReferenceCountedArray +*/ class JUCE_API DummyCriticalSection { public: @@ -1929,11 +3387,13 @@ public: inline void enter() const throw() {} inline void exit() const throw() {} + /** A dummy scoped-lock type to use with a dummy critical section. */ struct ScopedLockType { ScopedLockType (const DummyCriticalSection&) throw() {} }; + /** A dummy scoped-unlocker type to use with a dummy critical section. */ typedef ScopedLockType ScopedUnlockType; private: @@ -1944,6 +3404,29 @@ private: #endif // __JUCE_CRITICALSECTION_JUCEHEADER__ /*** End of inlined file: juce_CriticalSection.h ***/ +/** + Holds a list of simple objects, such as ints, doubles, or pointers. + + Examples of arrays are: Array, Array or Array + + The array can be used to hold simple, non-polymorphic objects as well as primitive types - to + do so, the class must fulfil these requirements: + - it must have a copy constructor and operator= + - it must be able to be relocated in memory by a memcpy without this causing a problem - so no + objects whose functionality relies on pointers or references to themselves can be used. + + You can of course have an array of pointers to any kind of object, e.g. Array , but if + you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the + ReferenceCountedArray class for more powerful ways of holding lists of objects. + + For holding lists of strings, you can use Array\, but it's usually better to use the + specialised class StringArray, which provides more useful functions. + + 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 @@ -1957,11 +3440,15 @@ private: public: + /** Creates an empty array. */ Array() throw() : numUsed (0) { } + /** Creates a copy of another array. + @param other the array to copy + */ Array (const Array& other) { const ScopedLockType lock (other.getLock()); @@ -1972,6 +3459,10 @@ public: new (data.elements + i) ElementType (other.data.elements[i]); } + /** Initalises from a null-terminated C array of values. + + @param values the array to copy from + */ explicit Array (const ElementType* values) : numUsed (0) { @@ -1979,6 +3470,11 @@ public: 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) : numUsed (numValues) { @@ -1988,12 +3484,16 @@ public: new (data.elements + i) ElementType (values[i]); } + /** Destructor. */ ~Array() { for (int i = 0; i < numUsed; ++i) data.elements[i].~ElementType(); } + /** Copies another array. + @param other the array to copy + */ Array& operator= (const Array& other) { if (this != &other) @@ -2005,6 +3505,11 @@ public: 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 { @@ -2020,12 +3525,24 @@ public: 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 { 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() { const ScopedLockType lock (getLock()); @@ -2037,6 +3554,10 @@ public: numUsed = 0; } + /** Removes all elements from the array without freeing the array's allocated storage. + + @see clear + */ void clearQuick() { const ScopedLockType lock (getLock()); @@ -2047,11 +3568,23 @@ public: numUsed = 0; } + /** 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 { const ScopedLockType lock (getLock()); @@ -2059,6 +3592,15 @@ public: : ElementType(); } + /** 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 const ElementType getUnchecked (const int index) const { const ScopedLockType lock (getLock()); @@ -2066,6 +3608,15 @@ public: return data.elements [index]; } + /** 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() { const ScopedLockType lock (getLock()); @@ -2073,6 +3624,10 @@ public: return data.elements [index]; } + /** Returns the first element in the array, or 0 if the array is empty. + + @see operator[], getUnchecked, getLast + */ inline ElementType getFirst() const { const ScopedLockType lock (getLock()); @@ -2080,6 +3635,10 @@ public: : ElementType(); } + /** Returns the last element in the array, or 0 if the array is empty. + + @see operator[], getUnchecked, getFirst + */ inline ElementType getLast() const { const ScopedLockType lock (getLock()); @@ -2087,11 +3646,23 @@ public: : ElementType(); } + /** Returns a pointer to the actual array data. + This pointer will only be valid until the next time a non-const method + is called on the array. + */ inline ElementType* getRawDataPointer() throw() { return data.elements; } + /** 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 (ParameterType elementToLookFor) const { const ScopedLockType lock (getLock()); @@ -2109,6 +3680,11 @@ public: return -1; } + /** 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 (ParameterType elementToLookFor) const { const ScopedLockType lock (getLock()); @@ -2126,6 +3702,11 @@ public: 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, addUsingDefaultSort, addArray + */ void add (ParameterType newElement) { const ScopedLockType lock (getLock()); @@ -2133,6 +3714,18 @@ public: new (data.elements + numUsed++) ElementType (newElement); } + /** 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, addUsingDefaultSort, set + */ void insert (int indexToInsertAt, ParameterType newElement) { const ScopedLockType lock (getLock()); @@ -2155,6 +3748,18 @@ public: } } + /** 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, ParameterType newElement, int numberOfTimesToInsertIt) { @@ -2182,6 +3787,18 @@ public: } } + /** 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) @@ -2210,6 +3827,14 @@ public: } } + /** 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 (ParameterType newElement) { const ScopedLockType lock (getLock()); @@ -2218,6 +3843,15 @@ public: add (newElement); } + /** 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, ParameterType newValue) { jassert (indexToChange >= 0); @@ -2234,6 +3868,15 @@ public: } } + /** 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, ParameterType newValue) { const ScopedLockType lock (getLock()); @@ -2241,6 +3884,12 @@ public: data.elements [indexToChange] = newValue; } + /** 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) { const ScopedLockType lock (getLock()); @@ -2254,6 +3903,11 @@ public: } } + /** 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. + */ void swapWithArray (Array& otherArray) throw() { const ScopedLockType lock1 (getLock()); @@ -2263,6 +3917,15 @@ public: swapVariables (numUsed, otherArray.numUsed); } + /** 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, @@ -2284,6 +3947,17 @@ public: add (arrayToAddFrom.getUnchecked (startIndex++)); } + /** 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 addUsingDefaultSort, add, sort + */ template void addSorted (ElementComparator& comparator, ParameterType newElement) { @@ -2291,12 +3965,33 @@ public: insert (findInsertIndexInSortedArray (comparator, data.elements.getData(), newElement, 0, numUsed), newElement); } + /** Inserts a new element into the array, assuming that the array is sorted. + + This will use the DefaultElementComparator class for sorting, so your ElementType + must be suitable for use with that class. If the array isn't sorted, the behaviour of this + method will be unpredictable. + + @param newElement the new element to insert to the array + @see addSorted, sort + */ void addUsingDefaultSort (ParameterType newElement) { DefaultElementComparator comparator; addSorted (comparator, newElement); } + /** 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, ParameterType elementToLookFor) const { @@ -2331,6 +4026,16 @@ public: } } + /** 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) { const ScopedLockType lock (getLock()); @@ -2358,6 +4063,14 @@ public: } } + /** 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 (ParameterType valueToRemove) { const ScopedLockType lock (getLock()); @@ -2375,6 +4088,18 @@ public: } } + /** 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, int numberToRemove) { const ScopedLockType lock (getLock()); @@ -2400,6 +4125,11 @@ public: } } + /** 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 (int howManyToRemove = 1) { const ScopedLockType lock (getLock()); @@ -2416,6 +4146,11 @@ public: minimiseStorageOverheads(); } + /** 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) { @@ -2437,6 +4172,13 @@ public: } } + /** 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) { @@ -2458,6 +4200,14 @@ public: } } + /** 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) { @@ -2471,6 +4221,20 @@ public: } } + /** 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) @@ -2503,18 +4267,56 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } + /** 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) { const ScopedLockType lock (getLock()); data.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 @@ -2525,8 +4327,13 @@ public: sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } + /** Returns the CriticalSection that locks this array. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ inline const TypeOfCriticalSectionToUse& getLock() const throw() { return data; } + /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; juce_UseDebuggingNewOperator @@ -2552,60 +4359,143 @@ private: class MemoryBlock; +/** + An arbitrarily large integer class. + + A BigInteger can be used in a similar way to a normal integer, but has no size + limit (except for memory and performance constraints). + + Negative values are possible, but the value isn't stored as 2s-complement, so + be careful if you use negative values and look at the values of individual bits. +*/ class JUCE_API BigInteger { public: + /** Creates an empty BigInteger */ BigInteger(); + /** Creates a BigInteger containing an integer value in its low bits. + + The low 32 bits of the number are initialised with this value. + */ BigInteger (unsigned int value); + /** Creates a BigInteger containing an integer value in its low bits. + + The low 32 bits of the number are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ BigInteger (int value); + /** Creates a BigInteger containing an integer value in its low bits. + + The low 64 bits of the number are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ BigInteger (int64 value); + /** Creates a copy of another BigInteger. */ BigInteger (const BigInteger& other); + /** Destructor. */ ~BigInteger(); + /** Copies another BigInteger onto this one. */ BigInteger& operator= (const BigInteger& other); + /** Swaps the internal contents of this with another object. */ void swapWith (BigInteger& other) throw(); + /** Returns the value of a specified bit in the number. + If the index is out-of-range, the result will be false. + */ bool operator[] (int bit) const throw(); + /** Returns true if no bits are set. */ bool isZero() const throw(); + /** Returns true if the value is 1. */ bool isOne() const throw(); + /** Attempts to get the lowest bits of the value as an integer. + If the value is bigger than the integer limits, this will return only the lower bits. + */ int toInteger() const throw(); + /** Resets the value to 0. */ void clear(); + /** Clears a particular bit in the number. */ void clearBit (int bitNumber) throw(); + /** Sets a specified bit to 1. */ void setBit (int bitNumber); + /** Sets or clears a specified bit. */ void setBit (int bitNumber, bool shouldBeSet); + /** 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, bool shouldBeSet); + /** Inserts a bit an a given position, shifting up any bits above it. */ void insertBit (int bitNumber, bool shouldBeSet); + /** Returns a range of bits as a new BigInteger. + + e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits. + @see getBitRangeAsInt + */ const BigInteger getBitRange (int startBit, int numBits) const; + /** Returns a range of bits 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) - for that, use + getBitRange(). + */ int getBitRangeAsInt (int startBit, int numBits) const throw(); + /** Sets a range of bits to an integer value. + + Copies the given integer onto a range of bits, starting at startBit, + and using up to numBits of the available bits. + */ void setBitRangeAsInt (int startBit, int numBits, unsigned int valueToSet); + /** 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); + /** Returns the total number of set bits in the value. */ int countNumberOfSetBits() const throw(); + /** Looks for the index of the next set bit after a given starting point. + + This 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. + + This 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 number. + If the value is zero, this will return -1. + */ int getHighestBit() const throw(); // All the standard arithmetic ops... @@ -2644,30 +4534,93 @@ public: bool operator> (const BigInteger& other) const throw(); bool operator>= (const BigInteger& other) const throw(); + /** Does a signed comparison of two BigIntegers. + + 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 BigInteger& other) const throw(); + /** Compares the magnitudes of two BigIntegers, 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 BigInteger& other) const throw(); + /** Divides this value by another one and returns the remainder. + + This number is divided by other, leaving the quotient in this number, + with the remainder being copied to the other BigInteger passed in. + */ void divideBy (const BigInteger& divisor, BigInteger& remainder); + /** Returns the largest value that will divide both this value and the one passed-in. + */ const BigInteger findGreatestCommonDivisor (BigInteger other) const; + /** Performs a combined exponent and modulo operation. + + This BigInteger's value becomes (this ^ exponent) % modulus. + */ void exponentModulo (const BigInteger& exponent, const BigInteger& modulus); + /** Performs an inverse modulo on the value. + + i.e. the result is (this ^ -1) mod (modulus). + */ void inverseModulo (const BigInteger& modulus); + /** 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(); + /** Converts the number to a string. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + If minimumNumCharacters is greater than 0, the returned string will be + padded with leading zeros to reach at least that length. + */ const String toString (int base, int minimumNumCharacters = 1) const; + /** Reads the numeric value from a string. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + Any invalid characters will be ignored. + */ void parseString (const String& text, int base); + /** Turns the number 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 number, and so on. + + @see loadFromMemoryBlock + */ const MemoryBlock toMemoryBlock() const; + /** Converts a block of raw data into a number. + + The data is arranged as little-endian, so the first byte of data is the low 8 bits + of the number, and so on. + + @see toMemoryBlock + */ void loadFromMemoryBlock (const MemoryBlock& data); juce_UseDebuggingNewOperator @@ -2681,8 +4634,11 @@ private: static const BigInteger simpleGCD (BigInteger* m, BigInteger* n); }; +/** Writes a BigInteger to an OutputStream as a UTF8 decimal string. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& value); +/** For backwards compatibility, BitArray is defined to be an alias for BigInteger. +*/ typedef BigInteger BitArray; #endif // __JUCE_BITARRAY_JUCEHEADER__ @@ -2721,73 +4677,192 @@ typedef BigInteger BitArray; #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 size_t 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 size_t 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. + */ 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 true if the data in this MemoryBlock matches the raw bytes passed-in. + */ bool matches (const void* data, size_t dataSize) const throw(); + /** 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. + */ template char& operator[] (const Type offset) const throw() { return data [offset]; } + /** Returns the block's current allocated size, in bytes. */ size_t 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 size_t 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 size_t 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 size_t numBytes) throw(); + /** Exchanges the contents of this and another memory block. + No actual copying is required for this, so it's very fast. + */ void swapWith (MemoryBlock& other) 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, size_t 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, size_t 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 (size_t startByte, size_t 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 (size_t bitRangeStart, size_t numBits, int binaryNumberToApply) throw(); + /** Reads a number of bits from the memory block, treating it as one long binary sequence */ int getBitRange (size_t bitRangeStart, size_t 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 @@ -2801,56 +4876,244 @@ private: #endif // __JUCE_MEMORYBLOCK_JUCEHEADER__ /*** End of inlined file: juce_MemoryBlock.h ***/ +/** 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 UTF8 string from the stream, up to the next linefeed or carriage return. + + 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 next 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 UTF8 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 @@ -2865,6 +5128,14 @@ protected: class File; +/** + 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 { protected: @@ -2872,64 +5143,177 @@ protected: 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 as a single byte. + This is encoded as a binary byte (not as text) with a value of 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 in a binary format. + 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 in a binary format. + 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 in a binary format. + 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 in a binary format. + 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 binary 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 in a binary format. + + 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 so that it can be retrieved later + by InputStream::readString(). + + It writes the string to the stream as UTF8, including the null termination character. + + For appending text to a file, instead use writeText, or operator<< + + @see InputStream::readString, writeText, operator<< + */ virtual void writeString (const String& text); + /** Writes a string of text to the stream. + + It can either write it as UTF8 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, bool asUnicode, bool writeUnicodeHeaderBytes); + /** 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, int64 maxNumBytesToWrite); juce_UseDebuggingNewOperator }; +/** Writes a number to a stream as 8-bit characters in the default system encoding. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int number); +/** Writes a number to a stream as 8-bit characters in the default system encoding. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, double number); +/** Writes a character to a stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, char character); +/** Writes a null-terminated text string to a stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* text); +/** Writes a block of data from a MemoryBlock to a stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data); +/** Writes the contents of a file to a stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead); #endif // __JUCE_OUTPUTSTREAM_JUCEHEADER__ @@ -2937,16 +5321,28 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileTo class JUCE_API DynamicObject; +/** + A variant class, that can be used to hold a range of primitive values. + + A var object can hold a range of simple primitive values, strings, or + a reference-counted pointer to a DynamicObject. The var class is intended + to act like the values used in dynamic scripting languages. + + @see DynamicObject +*/ class JUCE_API var { public: typedef const var (DynamicObject::*MethodFunction) (const var* arguments, int numArguments); + /** Creates a void variant. */ var() throw(); + /** Destructor. */ ~var() throw(); + /** A static var object that can be used where you need an empty variant object. */ static const var null; var (const var& valueToCopy); @@ -2987,19 +5383,36 @@ public: bool isObject() const throw() { return type == objectType; } bool isMethod() const throw() { return type == methodType; } + /** Writes a binary representation of this value to a stream. + The data can be read back later using readFromStream(). + */ void writeToStream (OutputStream& output) const; + /** Reads back a stored binary representation of a value. + The data in the stream must have been written using writeToStream(), or this + will have unpredictable results. + */ static const var readFromStream (InputStream& input); class JUCE_API identifier { public: + /** Creates a null identifier. */ identifier() throw(); + /** Creates an identifier with a specified name. + Because this name may need to be used in contexts such as script variables or XML + tags, it must only contain ascii letters and digits, or the underscore character. + */ identifier (const char* name); + /** Creates an identifier with a specified name. + Because this name may need to be used in contexts such as script variables or XML + tags, it must only contain ascii letters and digits, or the underscore character. + */ identifier (const String& name); + /** Destructor */ ~identifier(); bool operator== (const identifier& other) const throw() @@ -3012,21 +5425,31 @@ public: int hashCode; }; + /** If this variant is an object, this returns one of its properties. */ const var operator[] (const identifier& propertyName) const; + /** If this variant is an object, this invokes one of its methods with no arguments. */ const var call (const identifier& method) const; + /** If this variant is an object, this invokes one of its methods with one argument. */ const var call (const identifier& method, const var& arg1) const; + /** If this variant is an object, this invokes one of its methods with 2 arguments. */ const var call (const identifier& method, const var& arg1, const var& arg2) const; + /** If this variant is an object, this invokes one of its methods with 3 arguments. */ const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3); + /** If this variant is an object, this invokes one of its methods with 4 arguments. */ const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; + /** If this variant is an object, this invokes one of its methods with 5 arguments. */ const var call (const identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; + /** If this variant is an object, this invokes one of its methods with a list of arguments. */ const var invoke (const identifier& method, const var* arguments, int numArguments) const; + /** If this variant is a method pointer, this invokes it on a target object. */ const var invoke (const var& targetObject, const var* arguments, int numArguments) const; juce_UseDebuggingNewOperator + /** Returns true if this var has the same value as the one supplied. */ bool equals (const var& other) const throw(); private: @@ -3063,33 +5486,66 @@ bool operator!= (const var& v1, const String& v2) throw(); #endif // __JUCE_VARIANT_JUCEHEADER__ /*** End of inlined file: juce_Variant.h ***/ +/** Holds a set of named var objects. + + This can be used as a basic structure to hold a set of var object, which can + be retrieved by using their identifier. +*/ class JUCE_API NamedValueSet { public: + /** Creates an empty set. */ NamedValueSet() throw(); + /** Creates a copy of another set. */ NamedValueSet (const NamedValueSet& other); + /** Replaces this set with a copy of another set. */ NamedValueSet& operator= (const NamedValueSet& other); + /** Destructor. */ ~NamedValueSet(); + /** Returns the total number of values that the set contains. */ int size() const throw(); + /** Returns the value of a named item. + If the name isn't found, this will return a void variant. + @see getProperty + */ const var& operator[] (const var::identifier& name) const; + /** Tries to return the named value, but if no such value is found, this will + instead return the supplied default value. + */ const var getWithDefault (const var::identifier& name, const var& defaultReturnValue) const; + /** Returns a pointer to the object holding a named value, or + null if there is no value with this name. */ var* getItem (const var::identifier& name) const; + /** Changes or adds a named value. + @returns true if a value was changed or added; false if the + value was already set the the value passed-in. + */ bool set (const var::identifier& name, const var& newValue); + /** Returns true if the set contains an item with the specified name. */ bool contains (const var::identifier& name) const; + /** Removes a value from the set. + @returns true if a value was removed; false if there was no value + with the name that was given. + */ bool remove (const var::identifier& name); + /** Returns the name of the value at a given index. + The index must be between 0 and size() - 1. Out-of-range indexes will + return an empty identifier. + */ const var::identifier getName (int index) const; + /** Removes all values. */ void clear(); juce_UseDebuggingNewOperator @@ -3120,54 +5576,112 @@ private: #ifndef __JUCE_ATOMIC_JUCEHEADER__ #define __JUCE_ATOMIC_JUCEHEADER__ +/** + Simple class to hold a primitive value and perform atomic operations on it. + + The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. + There are methods to perform most of the basic atomic operations. +*/ template class Atomic { public: + /** Creates a new value, initialised to zero. */ inline Atomic() throw() : value (0) { } + /** Creates a new value, with a given initial value. */ inline Atomic (const Type initialValue) throw() : value (initialValue) { } + /** Copies another value (atomically). */ inline Atomic (const Atomic& other) throw() : value (other.get()) { } + /** Copies another value onto this one (atomically). */ inline Atomic& operator= (const Atomic& other) throw() { set (other.get()); } + /** Destructor. */ inline ~Atomic() throw() { // This class can only be used for types which are 32 or 64 bits in size. static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8); } + /** Atomically reads and returns the current value. */ Type get() const throw(); + /** Atomically sets the current value. */ void set (Type newValue) throw(); + /** Atomically sets the current value, returning the value that was replaced. */ Type exchange (Type value) throw(); + /** Atomically adds a number to this value, returning the new value. */ Type operator+= (Type amountToAdd) throw(); + /** Atomically subtracts a number from this value, returning the new value. */ Type operator-= (Type amountToSubtract) throw(); + /** Atomically increments this value, returning the new value. */ Type operator++() throw(); + /** Atomically decrements this value, returning the new value. */ Type operator--() throw(); + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + bool compareAndSetBool (Type newValue, Type valueToCompare) throw(); + { + if (get() == valueToCompare) + { + set (newValue); + return true; + } + + return false; + } + @endcode + + @returns true if the comparison was true and the value was replaced; false if + the comparison failed and the value was left unchanged. + @see compareAndSetValue + */ bool compareAndSetBool (Type newValue, Type valueToCompare) throw(); + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + Type compareAndSetValue (Type newValue, Type valueToCompare) throw(); + { + Type oldValue = get(); + if (oldValue == valueToCompare) + set (newValue); + + return oldValue; + } + @endcode + + @returns the old value before it was changed. + @see compareAndSetBool + */ Type compareAndSetValue (Type newValue, Type valueToCompare) throw(); + /** Performs a memory write barrier. */ static void memoryBarrier() throw(); #if JUCE_MSVC @@ -3176,9 +5690,16 @@ public: __attribute__ ((aligned (8))) #endif + /** The raw value that this class operates on. + This is exposed publically in case you need to manipulate it directly + for performance reasons. + */ Type value; }; +/* + The following code allows the atomics to be performed as inline functions where possible... +*/ #if (JUCE_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 || ! defined (__IPHONE_3_2))) \ || (JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier @@ -3349,15 +5870,51 @@ inline void Atomic::memoryBarrier() throw() #endif // __JUCE_ATOMIC_JUCEHEADER__ /*** End of inlined file: juce_Atomic.h ***/ +/** + 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() { ++refCount; } + /** Decreases the object's reference count. + + If the count gets to zero, the object will be deleted. + */ inline void decReferenceCount() throw() { jassert (getReferenceCount() > 0); @@ -3366,6 +5923,7 @@ public: delete this; } + /** Returns the object's current reference count. */ inline int getReferenceCount() const throw() { return refCount.get(); @@ -3373,10 +5931,12 @@ public: protected: + /** Creates the reference-counted object (with an initial ref count of zero). */ ReferenceCountedObject() { } + /** Destructor. */ virtual ~ReferenceCountedObject() { // it's dangerous to delete an object that's still referenced by something else! @@ -3388,16 +5948,31 @@ private: Atomic refCount; }; +/** + 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) { @@ -3405,6 +5980,10 @@ public: 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) { @@ -3412,6 +5991,11 @@ public: 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. + */ ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) { ReferenceCountedObjectClass* const newObject = other.referencedObject; @@ -3431,6 +6015,11 @@ public: 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. + */ ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectClass* const newObject) { if (referencedObject != newObject) @@ -3448,22 +6037,32 @@ public: 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; @@ -3475,6 +6074,9 @@ public: return referencedObject; } + /** Returns the object that this pointer references. + The pointer returned may be zero, of course. + */ inline ReferenceCountedObjectClass* getObject() const throw() { return referencedObject; @@ -3488,31 +6090,77 @@ private: #endif // __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ /*** End of inlined file: juce_ReferenceCountedObject.h ***/ +/** + Represents a dynamically implemented object. + + This class is primarily intended for wrapping scripting language objects, + but could be used for other purposes. + + An instance of a DynamicObject can be used to store named properties, and + by subclassing hasMethod() and invokeMethod(), you can give your object + methods. +*/ class JUCE_API DynamicObject : public ReferenceCountedObject { public: DynamicObject(); + /** Destructor. */ virtual ~DynamicObject(); + /** Returns true if the object has a property with this name. + Note that if the property is actually a method, this will return false. + */ virtual bool hasProperty (const var::identifier& propertyName) const; + /** Returns a named property. + + This returns a void if no such property exists. + */ virtual const var getProperty (const var::identifier& propertyName) const; + /** Sets a named property. */ virtual void setProperty (const var::identifier& propertyName, const var& newValue); + /** Removes a named property. */ virtual void removeProperty (const var::identifier& propertyName); + /** Checks whether this object has the specified method. + + The default implementation of this just checks whether there's a property + with this name that's actually a method, but this can be overridden for + building objects with dynamic invocation. + */ virtual bool hasMethod (const var::identifier& methodName) const; + /** Invokes a named method on this object. + + The default implementation looks up the named property, and if it's a method + call, then it invokes it. + + This method is virtual to allow more dynamic invocation to used for objects + where the methods may not already be set as properies. + */ virtual const var invokeMethod (const var::identifier& methodName, const var* parameters, int numParameters); + /** Sets up a method. + + This is basically the same as calling setProperty (methodName, (var::MethodFunction) myFunction), but + helps to avoid accidentally invoking the wrong type of var constructor. It also makes + the code easier to read, + + The compiler will probably force you to use an explicit cast your method to a (var::MethodFunction), e.g. + @code + setMethod ("doSomething", (var::MethodFunction) &MyClass::doSomething); + @endcode + */ void setMethod (const var::identifier& methodName, var::MethodFunction methodFunction); + /** Removes all properties and methods from the object. */ void clear(); juce_UseDebuggingNewOperator @@ -3549,28 +6197,69 @@ private: #ifndef __JUCE_SCOPEDPOINTER_JUCEHEADER__ #define __JUCE_SCOPEDPOINTER_JUCEHEADER__ +/** + This class holds a pointer which is automatically deleted when this object goes + out of scope. + + Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer + gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or + as member variables is a good way to use RAII to avoid accidentally leaking dynamically + created objects. + + A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer + to an object. If you use the assignment operator to assign a different object to a + ScopedPointer, the old one will be automatically deleted. + + A const ScopedPointer is guaranteed not to lose ownership of its object or change the + object to which it points during its lifetime. This means that making a copy of a const + ScopedPointer is impossible, as that would involve the new copy taking ownership from the + old one. + + If you need to get a pointer out of a ScopedPointer without it being deleted, you + can use the release() method. +*/ template class ScopedPointer { public: + /** Creates a ScopedPointer containing a null pointer. */ inline ScopedPointer() throw() : object (0) { } + /** Creates a ScopedPointer that owns the specified object. */ inline ScopedPointer (ObjectType* const objectToTakePossessionOf) throw() : object (objectToTakePossessionOf) { } + /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. + + Because a pointer can only belong to one ScopedPointer, this transfers + the pointer from the other object to this one, and the other object is reset to + be a null pointer. + */ ScopedPointer (ScopedPointer& objectToTransferFrom) throw() : object (objectToTransferFrom.object) { objectToTransferFrom.object = 0; } + /** Destructor. + This will delete the object that this ScopedPointer currently refers to. + */ inline ~ScopedPointer() { delete object; } + /** Changes this ScopedPointer to point to a new object. + + Because a pointer can only belong to one ScopedPointer, this transfers + the pointer from the other object to this one, and the other object is reset to + be a null pointer. + + If this ScopedPointer already points to an object, that object + will first be deleted. + */ ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) { if (this != objectToTransferFrom.getAddress()) @@ -3588,6 +6277,13 @@ public: return *this; } + /** Changes this ScopedPointer to point to a new object. + + If this ScopedPointer already points to an object, that object + will first be deleted. + + The pointer that you pass is may be null. + */ ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) { if (object != newObjectToTakePossessionOf) @@ -3600,18 +6296,32 @@ public: return *this; } + /** Returns the object that this ScopedPointer refers to. + */ inline operator ObjectType*() const throw() { return object; } + /** Returns the object that this ScopedPointer refers to. + */ inline ObjectType& operator*() const throw() { return *object; } + /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ inline ObjectType* operator->() const throw() { return object; } + /** Returns a reference to the address of the object that this ScopedPointer refers to. */ inline ObjectType* const* operator&() const throw() { return static_cast (&object); } + /** Returns a reference to the address of the object that this ScopedPointer refers to. */ inline ObjectType** operator&() throw() { return static_cast (&object); } + /** Removes the current object from this ScopedPointer without deleting it. + + This will return the current object, and set the ScopedPointer to a null pointer. + */ ObjectType* release() throw() { ObjectType* const o = object; object = 0; return o; } + /** Swaps this object with that of another ScopedPointer. + The two objects simply exchange their pointers. + */ void swapWith (ScopedPointer & other) throw() { // Two ScopedPointers should never be able to refer to the same object - if @@ -3635,12 +6345,18 @@ private: #endif }; +/** Compares a ScopedPointer with another pointer. + This can be handy for checking whether this is a null pointer. +*/ template inline bool operator== (const ScopedPointer& pointer1, const ObjectType* const pointer2) throw() { return static_cast (pointer1) == pointer2; } +/** Compares a ScopedPointer with another pointer. + This can be handy for checking whether this is a null pointer. +*/ template inline bool operator!= (const ScopedPointer& pointer1, const ObjectType* const pointer2) throw() { @@ -3650,6 +6366,24 @@ inline bool operator!= (const ScopedPointer& pointer1, const ObjectT #endif // __JUCE_SCOPEDPOINTER_JUCEHEADER__ /*** End of inlined file: juce_ScopedPointer.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 @@ -3657,16 +6391,23 @@ class OwnedArray { public: + /** Creates an empty array. */ OwnedArray() throw() : 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) { const ScopedLockType lock (getLock()); @@ -3681,11 +6422,22 @@ public: numUsed = 0; } + /** 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() { const ScopedLockType lock (getLock()); @@ -3693,6 +6445,11 @@ public: : static_cast (0); } + /** 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() { const ScopedLockType lock (getLock()); @@ -3700,6 +6457,11 @@ public: return data.elements [index]; } + /** 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() { const ScopedLockType lock (getLock()); @@ -3707,6 +6469,11 @@ public: : static_cast (0); } + /** 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() { const ScopedLockType lock (getLock()); @@ -3714,11 +6481,20 @@ public: : static_cast (0); } + /** Returns a pointer to the actual array data. + This pointer will only be valid until the next time a non-const method + is called on the array. + */ inline ObjectClass** getRawDataPointer() throw() { return data.elements; } + /** 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() { const ScopedLockType lock (getLock()); @@ -3736,6 +6512,11 @@ public: return -1; } + /** 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() { const ScopedLockType lock (getLock()); @@ -3753,6 +6534,17 @@ public: 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() { const ScopedLockType lock (getLock()); @@ -3760,6 +6552,23 @@ public: data.elements [numUsed++] = const_cast (newObject); } + /** 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() { @@ -3787,6 +6596,13 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); @@ -3795,6 +6611,19 @@ public: add (newObject); } + /** 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) @@ -3824,6 +6653,15 @@ public: } } + /** 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, @@ -3845,6 +6683,17 @@ public: add (arrayToAddFrom.getUnchecked (startIndex++)); } + /** 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() @@ -3855,6 +6704,18 @@ public: insert (findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed), newObject); } + /** 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() @@ -3890,6 +6751,16 @@ public: } } + /** 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) { @@ -3914,6 +6785,14 @@ public: } } + /** 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) { @@ -3932,6 +6811,19 @@ public: } } + /** 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) @@ -3967,6 +6859,12 @@ public: } } + /** 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) { @@ -3983,6 +6881,11 @@ public: } } + /** 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() { @@ -3996,6 +6899,19 @@ public: } } + /** 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() { @@ -4028,6 +6944,11 @@ public: } } + /** 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. + */ void swapWithArray (OwnedArray& otherArray) throw() { const ScopedLockType lock1 (getLock()); @@ -4037,18 +6958,55 @@ public: swapVariables (numUsed, otherArray.numUsed); } + /** 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() { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } + /** 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() { const ScopedLockType lock (getLock()); data.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() @@ -4060,8 +7018,13 @@ public: sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } + /** Returns the CriticalSection that locks this array. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ inline const TypeOfCriticalSectionToUse& getLock() const throw() { return data; } + /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; juce_UseDebuggingNewOperator @@ -4096,94 +7059,294 @@ private: #ifndef __JUCE_STRINGARRAY_JUCEHEADER__ #define __JUCE_STRINGARRAY_JUCEHEADER__ +/** + 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); + /** Creates an array containing a single string. */ explicit StringArray (const String& firstValue); + /** 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** strings, int numberOfStrings); + /** 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** strings, int numberOfStrings); + /** 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. + */ explicit StringArray (const juce_wchar** strings); + /** 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. + */ explicit StringArray (const char** strings); + /** Destructor. */ ~StringArray(); + /** Copies the contents of another string array into this one */ StringArray& operator= (const StringArray& other); + /** 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[] (int index) const throw(); + /** Returns a reference to one of the strings in the array. + This lets you modify a string in-place in the array, but you must be sure that + the index is in-range. + */ String& getReference (int index) 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, bool ignoreCase = false) const; + /** 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, bool ignoreCase = false, int startIndex = 0) const; + /** Appends a string at the end of the array. */ void add (const String& stringToAdd); + /** 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 (int index, const String& stringToAdd); + /** 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, bool ignoreCase = false); + /** 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 (int index, const String& newString); + /** 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); + /** 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 String& stringToTokenise, bool preserveQuotedStrings); + /** 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 String& stringToTokenise, const String& breakCharacters, const String& quoteCharacters); + /** 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 String& stringToBreakUp); + /** Removes all elements from the array. */ void clear(); + /** Removes a string from the array. + + If the index is out-of-range, no action will be taken. + */ void remove (int index); + /** 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, bool ignoreCase = false); + /** 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 + */ void removeRange (int startIndex, int numberToRemove); + /** 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 (bool ignoreCase); + /** Removes empty strings from the array. + + @param removeWhitespaceStrings if true, strings that only contain whitespace + characters will also be removed + */ void removeEmptyStrings (bool removeWhitespaceStrings = true); + /** 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 (int currentIndex, int newIndex) throw(); + /** Deletes any whitespace characters from the starts and ends of all the strings. */ void trim(); + /** 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. + If you pass 0, a default string will be used, which adds + brackets around the number. + @param postNumberString this string is appended after any numbers that are added. + If you pass 0, a default string will be used, which adds + brackets around the number. + */ void appendNumbersToDuplicates (bool ignoreCaseWhenComparing, bool appendNumberToFirstInstance, const juce_wchar* preNumberString = 0, const juce_wchar* postNumberString = 0); + /** 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; + /** Sorts the array into alphabetical order. + + @param ignoreCase if true, the comparisons used will be case-sensitive. + */ void sort (bool ignoreCase); + /** 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(); juce_UseDebuggingNewOperator @@ -4195,46 +7358,117 @@ private: #endif // __JUCE_STRINGARRAY_JUCEHEADER__ /*** End of inlined file: juce_StringArray.h ***/ +/** + 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 (bool ignoreCaseWhenComparingKeys = true); + /** Creates a copy of another array */ StringPairArray (const StringPairArray& other); + /** Destructor. */ ~StringPairArray(); + /** Copies the contents of another string array into this one */ StringPairArray& operator= (const StringPairArray& other); + /** 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; + /** 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; + /** 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; + /** 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); + /** 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(); + /** Removes a string from the array based on its key. + + If the key isn't found, nothing will happen. + */ void remove (const String& key); + /** Removes a string from the array based on its index. + + If the index is out-of-range, no action will be taken. + */ void remove (int index); + /** Indicates whether to use a case-insensitive search when looking up a key string. + */ void setIgnoresCase (bool shouldIgnoreCase); + /** Returns a descriptive string containing the items. + + This is handy for dumping the contents of an array. + */ const String getDescription() const; + /** 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(); juce_UseDebuggingNewOperator @@ -4267,63 +7501,155 @@ private: #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 (double seconds = 0.0) throw(); + /** Copies another relative time. */ RelativeTime (const RelativeTime& other) throw(); + /** Copies another relative time. */ 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 (int milliseconds) throw(); + /** Creates a new RelativeTime object representing a number of milliseconds. + + @see minutes, hours, days, weeks + */ static const RelativeTime milliseconds (int64 milliseconds) throw(); + /** Creates a new RelativeTime object representing a number of minutes. + + @see milliseconds, hours, days, weeks + */ static const RelativeTime minutes (double numberOfMinutes) throw(); + /** Creates a new RelativeTime object representing a number of hours. + + @see milliseconds, minutes, days, weeks + */ static const RelativeTime hours (double numberOfHours) throw(); + /** Creates a new RelativeTime object representing a number of days. + + @see milliseconds, minutes, hours, weeks + */ static const RelativeTime days (double numberOfDays) throw(); + /** Creates a new RelativeTime object representing a number of weeks. + + @see milliseconds, minutes, hours, days + */ static const RelativeTime weeks (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 = "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+ (double secondsToAdd) const throw(); + /** Subtracts a number of seconds from this RelativeTime and returns the result. */ const RelativeTime operator- (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+= (double secondsToAdd) throw(); + /** Subtracts a number of seconds from this time. */ const RelativeTime& operator-= (double secondsToSubtract) throw(); juce_UseDebuggingNewOperator @@ -4335,16 +7661,56 @@ private: #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 (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 + @param useLocalTime if true, encode using the current machine's local time; if + false, it will always work in GMT. + */ Time (int year, int month, int day, @@ -4354,95 +7720,291 @@ public: int milliseconds = 0, bool useLocalTime = true) throw(); + /** Destructor. */ ~Time() throw(); + /** Copies this time from another one. */ 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 (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 (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 (bool includeDate, bool includeTime, bool includeSeconds = true, 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 String& 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, 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, 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 (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 (int64 ticks) throw(); + /** Converts a number seconds into high-resolution ticks. + + @see getHighResolutionTicks, getHighResolutionTicksPerSecond, + highResolutionTicksToSeconds + */ static int64 secondsToHighResolutionTicks (double seconds) throw(); private: @@ -4456,110 +8018,466 @@ private: 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() {} + /** 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); + /** Creates a copy of another file object. */ File (const File& other); + /** Destructor. */ ~File() {} + /** 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. + */ File& operator= (const String& newFilePath); + /** Copies from another file object. */ File& operator= (const File& otherFile); + /** 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; + /** 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; + /** 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; + /** 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; + /** 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 (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; + /** 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; + /** 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; + /** 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. To compare with multiple extensions, this + parameter can contain multiple strings, separated by semi-colons - + so, for example: hasFileExtension (".jpeg;png;gif") would return + true if the file has any of those three extensions. + + @see getFileExtension, withFileExtension, getFileNameWithoutExtension + */ bool hasFileExtension (const String& extensionToTest) const; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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 (bool putNumbersInBrackets = true) const; + /** Compares the pathnames for two files. */ bool operator== (const File& otherFile) const; + /** Compares the pathnames for two files. */ bool operator!= (const File& otherFile) const; + /** Compares the pathnames for two files. */ bool operator< (const File& otherFile) const; + /** Compares the pathnames for two files. */ bool operator> (const File& otherFile) const; + /** 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; + /** 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 (bool shouldBeReadOnly, bool applyRecursively = false) const; + /** 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; + /** If this file is a link, this returns the file that it points to. + + If this file isn't actually link, it'll just return itself. + */ const File getLinkedTarget() const; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** 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; + /** If possible, this will try to create a version string for the given file. + + The OS may be able to look at the file and give a version for it - e.g. with + executables, bundles, dlls, etc. If no version is available, this will + return an empty string. + */ const String getVersion() const; + /** 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; + /** 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; + /** 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; + /** 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; + /** Moves this file or folder to the trash. + + @returns true if the operation succeeded. It could fail if the trash is full, or + if the file is write-protected, so you should check the return value + and act appropriately. + */ bool moveToTrash() const; + /** 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; + /** 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; + /** 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; + /** 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. */ @@ -4568,107 +8486,387 @@ public: 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 (Array& results, int whatToLookFor, bool searchRecursively, const String& wildCardPattern = "*") const; + /** 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 (int whatToLookFor, const String& wildCardPattern = "*") const; + /** Returns true if this file is a directory that contains one or more subdirectories. + @see isDirectory, findChildFiles + */ bool containsSubDirectories() const; + /** 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; + /** 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, appendData, appendText + */ FileOutputStream* createOutputStream (int bufferSize = 0x8000) const; + /** 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; + /** 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; + /** 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* dataToAppend, int numberOfBytes) const; + /** 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* dataToWrite, int numberOfBytes) const; + /** 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, bool asUnicode = false, bool writeUnicodeHeaderBytes = false) const; + /** 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, bool asUnicode = false, bool writeUnicodeHeaderBytes = false) const; + /** 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 (Array& results); + /** 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; + /** 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; + /** 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 + @see getVolumeTotalSize + */ int64 getBytesFreeOnVolume() const; + /** Returns the total size of the drive that contains this file. + + @returns the total number of bytes that the volume can hold + @see getBytesFreeOnVolume + */ int64 getVolumeTotalSize() const; + /** Returns true if this file is on a CD or DVD drive. */ bool isOnCDRomDrive() const; + /** 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; + /** 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; + /** 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. + + @see revealToUser + */ bool startAsProcess (const String& parameters = String::empty) const; + /** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location. + @see startAsProcess + */ void revealToUser() const; + /** 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. + + See also invokedExecutableFile, which is similar, but if the exe was launched from a + file link, invokedExecutableFile will return the name of the link. + */ 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, + /** Returns the file that was invoked to launch this executable. + This may differ from currentExecutableFile if the app was started from e.g. a link - this + will return the name of the link that was used, whereas currentExecutableFile will return + the actual location of the target executable. + */ invokedExecutableFile, + /** 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); + /** Returns the current working directory. + + @see setAsCurrentWorkingDirectory + */ static const File getCurrentWorkingDirectory(); + /** 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; + /** The system-specific file separator character. + + On Windows, this will be '\', on Mac/Linux, it'll be '/' + */ static const juce_wchar separator; + /** The system-specific file separator character, as a string. + + On Windows, this will be '\', on Mac/Linux, it'll be '/' + */ static const String 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); + /** 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); + /** 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); + /** Creates a file that simply contains this string, without doing the sanity-checking + that the normal constructors do. + + Best to avoid this unless you really know what you're doing. + */ static const File createFileWithoutCheckingPath (const String& path); + /** Adds a separator character to the end of a path if it doesn't already have one. */ static const String addTrailingSeparator (const String& path); juce_UseDebuggingNewOperator @@ -4697,39 +8895,180 @@ private: #endif // __JUCE_FILE_JUCEHEADER__ /*** End of inlined file: juce_File.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, "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. */ explicit XmlElement (const String& tagName) throw(); + /** Creates a (deep) copy of another element. */ XmlElement (const XmlElement& other) throw(); + /** Creates a (deep) copy of another element. */ 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* other, 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 String& 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 (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 (int attributeIndex) const throw(); // Attribute-handling methods.. + /** Checks whether the element contains an attribute with a certain name. */ bool hasAttribute (const String& attributeName) const throw(); + /** Returns the value of a named attribute. + + @param attributeName the name of the attribute to look up + */ const String& getStringAttribute (const String& 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 String& attributeName, const String& defaultReturnValue) const; + /** 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 String& attributeName, const String& stringToCompareAgainst, 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 + */ int getIntAttribute (const String& attributeName, int defaultReturnValue = 0) const; + /** 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 + */ double getDoubleAttribute (const String& attributeName, double defaultReturnValue = 0.0) const; + /** 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 String& attributeName, bool defaultReturnValue = false) const; + /** 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 String& attributeName, const String& newValue); + /** 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 String& attributeName, int newValue); + /** 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 String& attributeName, double newValue); + /** Removes a named attribute from the element. + + @param attributeName the name of the attribute to remove + @see removeAllAttributes + */ void removeAttribute (const String& 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 String& 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 (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 String& 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* newChildNode, int indexToInsertAt) throw(); + /** Creates a new element with the given name and returns it, after adding it + as a child element. + + This is a handy method that means that instead of writing this: + @code + XmlElement* newElement = new XmlElement ("foobar"); + myParentElement->addChildElement (newElement); + @endcode + + ..you could just write this: + @code + XmlElement* newElement = myParentElement->createNewChildElement ("foobar"); + @endcode + */ XmlElement* createNewChildElement (const String& tagName); + /** 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* currentChildElement, XmlElement* 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* childToRemove, 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 String& 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* 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() @@ -4837,21 +9461,74 @@ public: } } + /** 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 String& 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 @@ -4887,64 +9564,197 @@ private: #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. + */ 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 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: @@ -4966,26 +9776,36 @@ private: #ifndef __JUCE_RANGE_JUCEHEADER__ #define __JUCE_RANGE_JUCEHEADER__ +/** A general-purpose range object, that simply represents any linear range with + a start and end point. + + The templated parameter is expected to be a primitive integer or floating point + type, though class types could also be used if they behave in a number-like way. +*/ template class Range { public: + /** Constructs an empty range. */ Range() throw() : start (ValueType()), end (ValueType()) { } + /** Constructs a range with given start and end values. */ Range (const ValueType start_, const ValueType end_) throw() : start (start_), end (jmax (start_, end_)) { } + /** Constructs a copy of another range. */ Range (const Range& other) throw() : start (other.start), end (other.end) { } + /** Copies another range object. */ Range& operator= (const Range& other) throw() { start = other.start; @@ -4993,29 +9813,40 @@ public: return *this; } + /** Destructor. */ ~Range() throw() { } + /** Returns the range that lies between two positions (in either order). */ static const Range between (const ValueType position1, const ValueType position2) throw() { return (position1 < position2) ? Range (position1, position2) : Range (position2, position1); } + /** Returns a range with the specified start position and a length of zero. */ static const Range emptyRange (const ValueType start) throw() { return Range (start, start); } + /** Returns the start of the range. */ inline ValueType getStart() const throw() { return start; } + /** Returns the length of the range. */ inline ValueType getLength() const throw() { return end - start; } + /** Returns the end of the range. */ inline ValueType getEnd() const throw() { return end; } + /** Returns true if the range has a length of zero. */ inline bool isEmpty() const throw() { return start == end; } + /** Changes the start position of the range, leaving the end position unchanged. + If the new start position is higher than the current end of the range, the end point + will be pushed along to equal it, leaving an empty range at the new position. + */ void setStart (const ValueType newStart) throw() { start = newStart; @@ -5023,16 +9854,25 @@ public: end = newStart; } + /** Returns a range with the same end as this one, but a different start. + If the new start position is higher than the current end of the range, the end point + will be pushed along to equal it, returning an empty range at the new position. + */ const Range withStart (const ValueType newStart) const throw() { return Range (newStart, jmax (newStart, end)); } + /** Returns a range with the same length as this one, but moved to have the given start position. */ const Range movedToStartAt (const ValueType newStart) const throw() { return Range (newStart, newStart + getLength()); } + /** Changes the end position of the range, leaving the start unchanged. + If the new end position is below the current start of the range, the start point + will be pushed back to equal the new end point. + */ void setEnd (const ValueType newEnd) throw() { end = newEnd; @@ -5040,26 +9880,38 @@ public: start = newEnd; } + /** Returns a range with the same start position as this one, but a different end. + If the new end position is below the current start of the range, the start point + will be pushed back to equal the new end point. + */ const Range withEnd (const ValueType newEnd) const throw() { return Range (jmin (start, newEnd), newEnd); } + /** Returns a range with the same length as this one, but moved to have the given start position. */ const Range movedToEndAt (const ValueType newEnd) const throw() { return Range (newEnd - getLength(), newEnd); } + /** Changes the length of the range. + Lengths less than zero are treated as zero. + */ void setLength (const ValueType newLength) throw() { end = start + jmax (ValueType(), newLength); } + /** Returns a range with the same start as this one, but a different length. + Lengths less than zero are treated as zero. + */ const Range withLength (const ValueType newLength) const throw() { return Range (start, start + newLength); } + /** Adds an amount to the start and end of the range. */ inline const Range& operator+= (const ValueType amountToAdd) throw() { start += amountToAdd; @@ -5067,6 +9919,7 @@ public: return *this; } + /** Subtracts an amount from the start and end of the range. */ inline const Range& operator-= (const ValueType amountToSubtract) throw() { start -= amountToSubtract; @@ -5074,11 +9927,16 @@ public: return *this; } + /** Returns a range that is equal to this one with an amount added to its + start and end. + */ const Range operator+ (const ValueType amountToAdd) const throw() { return Range (start + amountToAdd, end + amountToAdd); } + /** Returns a range that is equal to this one with the specified amount + subtracted from its start and end. */ const Range operator- (const ValueType amountToSubtract) const throw() { return Range (start - amountToSubtract, end - amountToSubtract); @@ -5087,33 +9945,49 @@ public: bool operator== (const Range& other) const throw() { return start == other.start && end == other.end; } bool operator!= (const Range& other) const throw() { return start != other.start || end != other.end; } + /** Returns true if the given position lies inside this range. */ bool contains (const ValueType position) const throw() { return start <= position && position < end; } + /** Returns the nearest value to the one supplied, which lies within the range. */ ValueType clipValue (const ValueType value) const throw() { return jlimit (start, end, value); } + /** Returns true if the given range intersects this one. */ bool intersects (const Range& other) const throw() { return other.start < end && start < other.end; } + /** Returns the range that is the intersection of the two ranges, or an empty range + with an undefined start position if they don't overlap. */ const Range getIntersectionWith (const Range& other) const throw() { return Range (jmax (start, other.start), jmin (end, other.end)); } + /** Returns the smallest range that contains both this one and the other one. */ const Range getUnionWith (const Range& other) const throw() { return Range (jmin (start, other.start), jmax (end, other.end)); } + /** Returns a given range, after moving it forwards or backwards to fit it + within this range. + + If the supplied range has a greater length than this one, the return value + will be this range. + + Otherwise, if the supplied range is smaller than this one, the return value + will be the new range, shifted forwards or backwards so that it doesn't extend + beyond this one, but keeping its original length. + */ const Range constrainRange (const Range& rangeToConstrain) const throw() { const ValueType otherLen = rangeToConstrain.getLength(); @@ -5139,16 +10013,32 @@ private: #ifndef __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ #define __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ +/** + 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 { public: + /** Creates an empty array. + @see ReferenceCountedObject, Array, OwnedArray + */ ReferenceCountedArray() throw() : numUsed (0) { } + /** Creates a copy of another array */ ReferenceCountedArray (const ReferenceCountedArray& other) throw() { const ScopedLockType lock (other.getLock()); @@ -5161,6 +10051,10 @@ public: data.elements[i]->incReferenceCount(); } + /** Copies another array into this one. + + Any existing objects in this array will first be released. + */ ReferenceCountedArray& operator= (const ReferenceCountedArray& other) throw() { if (this != &other) @@ -5172,11 +10066,19 @@ public: 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() { const ScopedLockType lock (getLock()); @@ -5189,11 +10091,20 @@ public: data.setAllocatedSize (0); } + /** 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 const ReferenceCountedObjectPtr operator[] (const int index) const throw() { const ScopedLockType lock (getLock()); @@ -5201,6 +10112,11 @@ public: : static_cast (0); } + /** 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 const ReferenceCountedObjectPtr getUnchecked (const int index) const throw() { const ScopedLockType lock (getLock()); @@ -5208,6 +10124,11 @@ public: return data.elements [index]; } + /** Returns a pointer to the first object in the array. + + This will return a null pointer if the array's empty. + @see getLast + */ inline const ReferenceCountedObjectPtr getFirst() const throw() { const ScopedLockType lock (getLock()); @@ -5215,6 +10136,11 @@ public: : static_cast (0); } + /** Returns a pointer to the last object in the array. + + This will return a null pointer if the array's empty. + @see getFirst + */ inline const ReferenceCountedObjectPtr getLast() const throw() { const ScopedLockType lock (getLock()); @@ -5222,6 +10148,11 @@ public: : static_cast (0); } + /** 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() { const ScopedLockType lock (getLock()); @@ -5239,6 +10170,11 @@ public: return -1; } + /** 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() { const ScopedLockType lock (getLock()); @@ -5256,6 +10192,13 @@ public: 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() { const ScopedLockType lock (getLock()); @@ -5266,6 +10209,19 @@ public: newObject->incReferenceCount(); } + /** 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() { @@ -5297,6 +10253,13 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); @@ -5304,6 +10267,18 @@ public: add (newObject); } + /** 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) { @@ -5329,6 +10304,15 @@ public: } } + /** 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() @@ -5356,6 +10340,17 @@ public: 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() @@ -5364,6 +10359,11 @@ public: insert (findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed), newObject); } + /** Inserts or replaces an object in the array, assuming it is sorted. + + This is similar to addSorted, but if a matching element already exists, then it will be + replaced by the new one, rather than the new one being added as well. + */ template void addOrReplaceSorted (ElementComparator& comparator, ObjectClass* newObject) throw() @@ -5377,6 +10377,19 @@ public: insert (index, newObject); // no match, so insert the new one } + /** 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) { const ScopedLockType lock (getLock()); @@ -5399,12 +10412,35 @@ public: } } + /** 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) { const ScopedLockType lock (getLock()); remove (indexOf (objectToRemove)); } + /** 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) { @@ -5441,6 +10477,14 @@ public: } } + /** 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) { const ScopedLockType lock (getLock()); @@ -5452,6 +10496,11 @@ public: remove (numUsed - 1); } + /** 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() { @@ -5465,6 +10514,19 @@ public: } } + /** 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() { @@ -5497,6 +10559,11 @@ public: } } + /** 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. + */ void swapWithArray (ReferenceCountedArray& otherArray) throw() { const ScopedLockType lock1 (getLock()); @@ -5506,6 +10573,10 @@ public: swapVariables (numUsed, otherArray.numUsed); } + /** 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() { const ScopedLockType lock2 (other.getLock()); @@ -5521,11 +10592,41 @@ public: return true; } + /** 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() @@ -5537,14 +10638,25 @@ public: sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } + /** 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() { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } + /** Returns the CriticalSection that locks this array. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ inline const TypeOfCriticalSectionToUse& getLock() const throw() { return data; } + /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; juce_UseDebuggingNewOperator @@ -5576,16 +10688,41 @@ private: #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 { public: + /** Creates an empty set. */ SortedSet() throw() : numUsed (0) { } + /** Creates a copy of another set. + @param other the set to copy + */ SortedSet (const SortedSet& other) throw() { const ScopedLockType lock (other.getLock()); @@ -5594,10 +10731,14 @@ public: memcpy (data.elements, other.data.elements, numUsed * sizeof (ElementType)); } + /** Destructor. */ ~SortedSet() throw() { } + /** Copies another set over this one. + @param other the set to copy + */ SortedSet& operator= (const SortedSet& other) throw() { if (this != &other) @@ -5614,6 +10755,13 @@ public: 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() { const ScopedLockType lock (getLock()); @@ -5628,11 +10776,26 @@ public: 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() { const ScopedLockType lock (getLock()); @@ -5640,17 +10803,34 @@ public: numUsed = 0; } + /** Removes all elements from the set without freeing the array's allocated storage. + + @see clear + */ void clearQuick() throw() { const ScopedLockType lock (getLock()); numUsed = 0; } + /** 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() { const ScopedLockType lock (getLock()); @@ -5658,6 +10838,14 @@ public: : ElementType(); } + /** 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() { const ScopedLockType lock (getLock()); @@ -5665,18 +10853,34 @@ public: return data.elements [index]; } + /** Returns the first element in the set, or 0 if the set is empty. + + @see operator[], getUnchecked, getLast + */ inline ElementType getFirst() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [0] : ElementType(); } + /** Returns the last element in the set, or 0 if the set is empty. + + @see operator[], getUnchecked, getFirst + */ inline ElementType getLast() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [numUsed - 1] : ElementType(); } + /** 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() { const ScopedLockType lock (getLock()); @@ -5708,6 +10912,11 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); @@ -5739,6 +10948,11 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); @@ -5779,6 +10993,12 @@ public: } } + /** 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() { @@ -5788,6 +11008,15 @@ public: add (*elementsToAdd++); } + /** 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, @@ -5812,6 +11041,15 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); @@ -5836,12 +11074,24 @@ public: 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() { const ScopedLockType lock (getLock()); remove (indexOf (valueToRemove)); } + /** 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() { @@ -5863,6 +11113,13 @@ public: } } + /** 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() { @@ -5884,14 +11141,25 @@ public: } } + /** 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() { const ScopedLockType lock (getLock()); data.shrinkToNoMoreThan (numUsed); } + /** Returns the CriticalSection that locks this array. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ inline const TypeOfCriticalSectionToUse& getLock() const throw() { return data; } + /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; juce_UseDebuggingNewOperator @@ -5930,34 +11198,58 @@ private: #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 an array, 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() { } + /** Creates a copy of another SparseSet. */ SparseSet (const SparseSet& other) : values (other.values) { } + /** Destructor. */ ~SparseSet() { } + /** Clears the set. */ void clear() { 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 { Type total (0); @@ -5968,6 +11260,11 @@ public: return total; } + /** 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[] (Type index) const { for (int i = 0; i < values.size(); i += 2) @@ -5984,6 +11281,7 @@ public: return Type (0); } + /** Checks whether a particular value is in the set. */ bool contains (const Type valueToLookFor) const { for (int i = 0; i < values.size(); ++i) @@ -5993,11 +11291,19 @@ public: 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) + @see getTotalRange + */ const Range getRange (const int rangeIndex) const { if (((unsigned int) rangeIndex) < (unsigned int) getNumRanges()) @@ -6007,6 +11313,9 @@ public: return Range(); } + /** Returns the range between the lowest and highest values in the set. + @see getRange + */ const Range getTotalRange() const { if (values.size() > 0) @@ -6019,6 +11328,9 @@ public: return Range(); } + /** Adds a range of contiguous values to the set. + e.g. addRange (Range \ (10, 14)) will add (10, 11, 12, 13) to the set. + */ void addRange (const Range& range) { jassert (range.getLength() >= 0); @@ -6033,6 +11345,9 @@ public: } } + /** Removes a range of values from the set. + e.g. removeRange (Range\ (10, 14)) will remove (10, 11, 12, 13) from the set. + */ void removeRange (const Range& rangeToRemove) { jassert (rangeToRemove.getLength() >= 0); @@ -6069,6 +11384,7 @@ public: } } + /** Does an XOR of the values in a given range. */ void invertRange (const Range& range) { SparseSet newItems; @@ -6084,6 +11400,7 @@ public: addRange (newItems.getRange(i)); } + /** Checks whether any part of a given range overlaps any part of this set. */ bool overlapsRange (const Range& range) { if (range.getLength() > 0) @@ -6101,6 +11418,7 @@ public: return false; } + /** Checks whether the whole of a given range is contained within this one. */ bool containsRange (const Range& range) { if (range.getLength() > 0) @@ -6174,17 +11492,35 @@ private: class MessageListener; class MessageManager; +/** 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 (int intParameter1, int intParameter2, int intParameter3, void* pointerParameter) throw(); + /** Destructor. */ virtual ~Message() throw(); // These values can be used for carrying simple data that the application needs to @@ -6209,40 +11545,121 @@ private: #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* 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 ***/ +/** + 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: @@ -6274,6 +11691,43 @@ private: #ifndef __JUCE_LISTENERLIST_JUCEHEADER__ #define __JUCE_LISTENERLIST_JUCEHEADER__ +/** + Holds a set of objects and can invoke a member function callback on each object + in the set with a single call. + + Use a ListenerList to manage a set of objects which need a callback, and you + can invoke a member function by simply calling call() or callChecked(). + + E.g. + @code + class MyListenerType + { + public: + void myCallbackMethod (int foo, bool bar); + }; + + ListenerList listeners; + listeners.add (someCallbackObjects...); + + // This will invoke myCallbackMethod (1234, true) on each of the objects + // in the list... + listeners.call (&MyListenerType::myCallbackMethod, 1234, true); + @endcode + + If you add or remove listeners from the list during one of the callbacks - i.e. while + it's in the middle of iterating the listeners, then it's guaranteed that no listeners + will be mistakenly called after they've been removed, but it may mean that some of the + listeners could be called more than once, or not at all, depending on the list's order. + + Sometimes, there's a chance that invoking one of the callbacks might result in the + list itself being deleted while it's still iterating - to survive this situation, you can + use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker". + The BailOutChecker must implement a method of the form "bool shouldBailOut()", and + the list will check this after each callback to determine whether it should abort the + operation. For an example of a bail-out checker, see the Component::BailOutChecker class, + which can be used to check when a Component has been deleted. See also + ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. +*/ template > class ListenerList @@ -6289,14 +11743,21 @@ class ListenerList public: + /** Creates an empty list. */ ListenerList() { } + /** Destructor. */ ~ListenerList() { } + /** Adds a listener to the list. + A listener can only be added once, so if the listener is already in the list, + this method has no effect. + @see remove + */ void add (ListenerClass* const listenerToAdd) { // Listeners can't be null pointers! @@ -6306,6 +11767,9 @@ public: listeners.addIfNotAlreadyThere (listenerToAdd); } + /** Removes a listener from the list. + If the listener wasn't in the list, this has no effect. + */ void remove (ListenerClass* const listenerToRemove) { // Listeners can't be null pointers! @@ -6314,26 +11778,32 @@ public: listeners.removeValue (listenerToRemove); } + /** Returns the number of registered listeners. */ int size() const throw() { return listeners.size(); } + /** Returns true if any listeners are registered. */ bool isEmpty() const throw() { return listeners.size() == 0; } + /** Returns true if the specified listener has been added to the list. */ bool contains (ListenerClass* const listener) const throw() { return listeners.contains (listener); } + /** Calls a member function on each listener in the list, with no parameters. */ void call (void (ListenerClass::*callbackFunction) ()) { callChecked (static_cast (DummyBailOutChecker()), callbackFunction); } + /** Calls a member function on each listener in the list, with no parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ()) @@ -6342,6 +11812,7 @@ public: (iter.getListener()->*callbackFunction) (); } + /** Calls a member function on each listener in the list, with 1 parameter. */ template void call (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) { @@ -6349,6 +11820,8 @@ public: (iter.getListener()->*callbackFunction) (param1); } + /** Calls a member function on each listener in the list, with one parameter and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1), @@ -6358,6 +11831,7 @@ public: (iter.getListener()->*callbackFunction) (param1); } + /** Calls a member function on each listener in the list, with 2 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2), LL_PARAM(1), LL_PARAM(2)) @@ -6366,6 +11840,8 @@ public: (iter.getListener()->*callbackFunction) (param1, param2); } + /** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2), @@ -6375,6 +11851,7 @@ public: (iter.getListener()->*callbackFunction) (param1, param2); } + /** Calls a member function on each listener in the list, with 3 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) @@ -6383,6 +11860,8 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3); } + /** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3), @@ -6392,6 +11871,7 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3); } + /** Calls a member function on each listener in the list, with 4 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) @@ -6400,6 +11880,8 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); } + /** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), @@ -6409,6 +11891,7 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); } + /** Calls a member function on each listener in the list, with 5 parameters. */ template void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) @@ -6417,6 +11900,8 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); } + /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), @@ -6426,12 +11911,16 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); } + /** A dummy bail-out checker that always returns false. + See the ListenerList notes for more info about bail-out checkers. + */ class DummyBailOutChecker { public: inline bool shouldBailOut() const throw() { return false; } }; + /** Iterates the listeners in a ListenerList. */ template class Iterator { @@ -6490,49 +11979,139 @@ private: #endif // __JUCE_LISTENERLIST_JUCEHEADER__ /*** End of inlined file: juce_ListenerList.h ***/ +/** + Represents a shared variant value. + + A Value object contains a reference to a var object, and can get and set its value. + Listeners can be attached to be told when the value is changed. + + The Value class is a wrapper around a shared, reference-counted underlying data + object - this means that multiple Value objects can all refer to the same piece of + data, allowing all of them to be notified when any of them changes it. + + The base class of Value contains a simple var object, but subclasses can be + created that map a Value onto any kind of underlying data, e.g. + ValueTree::getPropertyAsValue() returns a Value object that is a wrapper + for one of its properties. +*/ class JUCE_API Value { public: + /** Creates an empty Value, containing a void var. */ Value(); + /** Creates a Value that refers to the same value as another one. + + Note that this doesn't make a copy of the other value - both this and the other + Value will share the same underlying value, so that when either one alters it, both + will see it change. + */ Value (const Value& other); + /** Creates a Value that is set to the specified value. */ explicit Value (const var& initialValue); + /** Destructor. */ ~Value(); + /** Returns the current value. */ const var getValue() const; + /** Returns the current value. */ operator const var() const; + /** Returns the value as a string. + + This is alternative to writing things like "myValue.getValue().toString()". + */ const String toString() const; + /** Sets the current value. + + You can also use operator= to set the value. + + If there are any listeners registered, they will be notified of the + change asynchronously. + */ void setValue (const var& newValue); + /** Sets the current value. + + This is the same as calling setValue(). + + If there are any listeners registered, they will be notified of the + change asynchronously. + */ Value& operator= (const var& newValue); + /** Makes this object refer to the same underlying ValueSource as another one. + + Once this object has been connected to another one, changing either one + will update the other. + + Existing listeners will still be registered after you call this method, and + they'll continue to receive messages when the new value changes. + */ void referTo (const Value& valueToReferTo); + /** Returns true if this value and the other one are references to the same value. + */ bool refersToSameSourceAs (const Value& other) const; + /** Compares two values. + This is a compare-by-value comparison, so is effectively the same as + saying (this->getValue() == other.getValue()). + */ bool operator== (const Value& other) const; + /** Compares two values. + This is a compare-by-value comparison, so is effectively the same as + saying (this->getValue() != other.getValue()). + */ bool operator!= (const Value& other) const; + /** Receives callbacks when a Value object changes. + @see Value::addListener + */ class JUCE_API Listener { public: Listener() {} virtual ~Listener() {} + /** Called when a Value object is changed. + + Note that the Value object passed as a parameter may not be exactly the same + object that you registered the listener with - it might be a copy that refers + to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs(). + */ virtual void valueChanged (Value& value) = 0; }; + /** Adds a listener to receive callbacks when the value changes. + + The listener is added to this specific Value object, and not to the shared + object that it refers to. When this object is deleted, all the listeners will + be lost, even if other references to the same Value still exist. So when you're + adding a listener, make sure that you add it to a ValueTree instance that will last + for as long as you need the listener. In general, you'd never want to add a listener + to a local stack-based ValueTree, but more likely to one that's a member variable. + + @see removeListener + */ void addListener (Listener* const listener); + /** Removes a listener that was previously added with addListener(). */ void removeListener (Listener* const listener); + /** + Used internally by the Value class as the base class for its shared value objects. + + The Value class is essentially a reference-counted pointer to a shared instance + of a ValueSource object. If you're feeling adventurous, you can create your own custom + ValueSource classes to allow Value objects to represent your own custom data items. + */ class JUCE_API ValueSource : public ReferenceCountedObject, public AsyncUpdater { @@ -6540,9 +12119,19 @@ public: ValueSource(); virtual ~ValueSource(); + /** Returns the current value of this object. */ virtual const var getValue() const = 0; + /** Changes the current value. + This must also trigger a change message if the value actually changes. + */ virtual void setValue (const var& newValue) = 0; + /** Delivers a change message to all the listeners that are registered with + this value. + + If dispatchSynchronously is true, the method will call all the listeners + before returning; otherwise it'll dispatch a message and make the call later. + */ void sendChangeMessage (const bool dispatchSynchronously); juce_UseDebuggingNewOperator @@ -6557,7 +12146,9 @@ public: ValueSource& operator= (const ValueSource&); }; + /** @internal */ explicit Value (ValueSource* const valueSource); + /** @internal */ ValueSource& getValueSource() { return *value; } juce_UseDebuggingNewOperator @@ -6574,6 +12165,7 @@ private: Value& operator= (const Value& other); }; +/** Writes a Value to an OutputStream as a UTF8 string. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value); #endif // __JUCE_VALUE_JUCEHEADER__ @@ -6607,11 +12199,30 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value #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; }; @@ -6623,12 +12234,51 @@ public: #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 explicit 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: @@ -6639,12 +12289,66 @@ private: 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 explicit 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: @@ -6658,26 +12362,71 @@ private: #endif // __JUCE_SCOPEDLOCK_JUCEHEADER__ /*** End of inlined file: juce_ScopedLock.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* 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* 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 @@ -6695,24 +12444,57 @@ private: #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* 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* 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: @@ -6731,63 +12513,239 @@ private: #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 (int maxNumberOfUnitsToKeep = 30000, 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 (int maxNumberOfUnitsToKeep, 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* 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 the number of UndoableAction objects that have been performed during the + transaction that is currently open. + @see getActionsInCurrentTransaction + */ int getNumActionsInCurrentTransaction() 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 @@ -6808,101 +12766,366 @@ private: #endif // __JUCE_UNDOMANAGER_JUCEHEADER__ /*** End of inlined file: juce_UndoManager.h ***/ +/** + A powerful tree structure that can be used to hold free-form data, and which can + handle its own undo and redo behaviour. + + A ValueTree contains a list of named properties as var objects, and also holds + any number of sub-trees. + + Create ValueTree objects on the stack, and don't be afraid to copy them around, as + they're simply a lightweight reference to a shared data container. Creating a copy + of another ValueTree simply creates a new reference to the same underlying object - to + make a separate, deep copy of a tree you should explicitly call createCopy(). + + Each ValueTree has a type name, in much the same way as an XmlElement has a tag name, + and much of the structure of a ValueTree is similar to an XmlElement tree. + You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't + contain text elements, the conversion works well and makes a good serialisation + format. They can also be serialised to a binary format, which is very fast and compact. + + All the methods that change data take an optional UndoManager, which will be used + to track any changes to the object. For this to work, you have to be careful to + consistently always use the same UndoManager for all operations to any node inside + the tree. + + A ValueTree can only be a child of one parent at a time, so if you're moving one from + one tree to another, be careful to always remove it first, before adding it. This + could also mess up your undo/redo chain, so be wary! In a debug build you should hit + assertions if you try to do anything dangerous, but there are still plenty of ways it + could go wrong. + + Listeners can be added to a ValueTree to be told when properies change and when + nodes are added or removed. + + @see var, XmlElement +*/ class JUCE_API ValueTree { public: + /** Creates an empty, invalid ValueTree. + + A ValueTree that is created with this constructor can't actually be used for anything, + it's just a default 'null' ValueTree that can be returned to indicate some sort of failure. + To create a real one, use the constructor that takes a string. + + @see ValueTree::invalid + */ ValueTree() throw(); + /** Creates an empty ValueTree with the given type name. + Like an XmlElement, each ValueTree node has a type, which you can access with + getType() and hasType(). + */ explicit ValueTree (const String& type); + /** Creates a reference to another ValueTree. */ ValueTree (const ValueTree& other); + /** Makes this object reference another node. */ ValueTree& operator= (const ValueTree& other); + /** Destructor. */ ~ValueTree(); + /** Returns true if both this and the other tree node refer to the same underlying structure. + Note that this isn't a value comparison - two independently-created trees which + contain identical data are not considered equal. + */ bool operator== (const ValueTree& other) const; + /** Returns true if this and the other node refer to different underlying structures. + Note that this isn't a value comparison - two independently-created trees which + contain identical data are not considered equal. + */ bool operator!= (const ValueTree& other) const; + /** Returns true if this node refers to some valid data. + It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range + call to getChild(). + */ bool isValid() const { return object != 0; } + /** Returns a deep copy of this tree and all its sub-nodes. */ ValueTree createCopy() const; + /** Returns the type of this node. + The type is specified when the ValueTree is created. + @see hasType + */ const String getType() const; + /** Returns true if the node has this type. + The comparison is case-sensitive. + */ bool hasType (const String& typeName) const; + /** Returns the value of a named property. + If no such property has been set, this will return a void variant. + You can also use operator[] to get a property. + @see var, setProperty, hasProperty + */ const var& getProperty (const var::identifier& name) const; + /** Returns the value of a named property, or a user-specified default if the property doesn't exist. + If no such property has been set, this will return the value of defaultReturnValue. + You can also use operator[] and getProperty to get a property. + @see var, getProperty, setProperty, hasProperty + */ const var getProperty (const var::identifier& name, const var& defaultReturnValue) const; + /** Returns the value of a named property. + If no such property has been set, this will return a void variant. This is the same as + calling getProperty(). + @see getProperty + */ const var& operator[] (const var::identifier& name) const; + /** Changes a named property of the node. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + @see var, getProperty, removeProperty + */ void setProperty (const var::identifier& name, const var& newValue, UndoManager* undoManager); + /** Returns true if the node contains a named property. */ bool hasProperty (const var::identifier& name) const; + /** Removes a property from the node. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void removeProperty (const var::identifier& name, UndoManager* undoManager); + /** Removes all properties from the node. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void removeAllProperties (UndoManager* undoManager); + /** Returns the total number of properties that the node contains. + @see getProperty. + */ int getNumProperties() const; + /** Returns the identifier of the property with a given index. + @see getNumProperties + */ const var::identifier getPropertyName (int index) const; + /** Returns a Value object that can be used to control and respond to one of the tree's properties. + + The Value object will maintain a reference to this tree, and will use the undo manager when + it needs to change the value. Attaching a Value::Listener to the value object will provide + callbacks whenever the property changes. + */ Value getPropertyAsValue (const var::identifier& name, UndoManager* undoManager) const; + /** Returns the number of child nodes belonging to this one. + @see getChild + */ int getNumChildren() const; + /** Returns one of this node's child nodes. + If the index is out of range, it'll return an invalid node. (See isValid() to find out + whether a node is valid). + */ ValueTree getChild (int index) const; + /** Looks for a child node with the speficied type name. + If no such node is found, it'll return an invalid node. (See isValid() to find out + whether a node is valid). + */ ValueTree getChildWithName (const String& type) const; + /** Looks for the first child node that has the speficied property value. + + This will scan the child nodes in order, until it finds one that has property that matches + the specified value. + + If no such node is found, it'll return an invalid node. (See isValid() to find out + whether a node is valid). + */ ValueTree getChildWithProperty (const var::identifier& propertyName, const var& propertyValue) const; + /** Adds a child to this node. + + Make sure that the child is removed from any former parent node before calling this, or + you'll hit an assertion. + + If the index is < 0 or greater than the current number of child nodes, the new node will + be added at the end of the list. + + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void addChild (ValueTree child, int index, UndoManager* undoManager); + /** Removes the specified child from this node's child-list. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void removeChild (const ValueTree& child, UndoManager* undoManager); + /** Removes a child from this node's child-list. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void removeChild (int childIndex, UndoManager* undoManager); + /** Removes all child-nodes from this node. + If the undoManager parameter is non-null, its UndoManager::perform() method will be used, + so that this change can be undone. + */ void removeAllChildren (UndoManager* undoManager); + /** Moves one of the children to a different index. + + This will move the child to a specified index, shuffling along any intervening + items as required. So for example, if you have a list of { 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 item 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 item to end up. If this + is less than zero, the value will be moved to the end + of the list + @param undoManager the optional UndoManager to use to store this transaction + */ void moveChild (int currentIndex, int newIndex, UndoManager* undoManager); + /** Returns true if this node is anywhere below the specified parent node. + This returns true if the node is a child-of-a-child, as well as a direct child. + */ bool isAChildOf (const ValueTree& possibleParent) const; + /** Returns the index of a child item in this parent. + If the child isn't found, this returns -1. + */ int indexOf (const ValueTree& child) const; + /** Returns the parent node that contains this one. + If the node has no parent, this will return an invalid node. (See isValid() to find out + whether a node is valid). + */ ValueTree getParent() const; + /** Creates an XmlElement that holds a complete image of this node and all its children. + + If this node is invalid, this may return 0. Otherwise, the XML that is produced can + be used to recreate a similar node by calling fromXml() + @see fromXml + */ XmlElement* createXml() const; + /** Tries to recreate a node from its XML representation. + + This isn't designed to cope with random XML data - for a sensible result, it should only + be fed XML that was created by the createXml() method. + */ static ValueTree fromXml (const XmlElement& xml); + /** Stores this tree (and all its children) in a binary format. + + Once written, the data can be read back with readFromStream(). + + It's much faster to load/save your tree in binary form than as XML, but + obviously isn't human-readable. + */ void writeToStream (OutputStream& output); + /** Reloads a tree from a stream that was written with writeToStream(). + */ static ValueTree readFromStream (InputStream& input); + /** Listener class for events that happen to a ValueTree. + + To get events from a ValueTree, make your class implement this interface, and use + ValueTree::addListener() and ValueTree::removeListener() to register it. + */ class JUCE_API Listener { public: + /** Destructor. */ virtual ~Listener() {} + /** This method is called when a property of this node (or of one of its sub-nodes) has + changed. + + The tree parameter indicates which tree has had its property changed, and the property + parameter indicates the property. + + Note that when you register a listener to a tree, it will receive this callback for + property changes in that tree, and also for any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + simply check the tree parameter in this callback to make sure it's the tree you're interested in. + */ virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) = 0; + /** This method is called when a child sub-tree is added or removed. + + The tree parameter indicates the tree whose child was added or removed. + + Note that when you register a listener to a tree, it will receive this callback for + child changes in that tree, and also in any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + simply check the tree parameter in this callback to make sure it's the tree you're interested in. + */ virtual void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) = 0; + /** This method is called when a tree has been added or removed from a parent node. + + This callback happens when the tree to which the listener was registered is added or + removed from a parent. Unlike the other callbacks, it applies only to the tree to which + the listener is registered, and not to any of its children. + */ virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0; }; + /** Adds a listener to receive callbacks when this node is changed. + + The listener is added to this specific ValueTree object, and not to the shared + object that it refers to. When this object is deleted, all the listeners will + be lost, even if other references to the same ValueTree still exist. And if you + use the operator= to make this refer to a different ValueTree, any listeners will + begin listening to changes to the new tree instead of the old one. + + When you're adding a listener, make sure that you add it to a ValueTree instance that + will last for as long as you need the listener. In general, you'd never want to add a + listener to a local stack-based ValueTree, and would usually add one to a member variable. + + @see removeListener + */ void addListener (Listener* listener); + /** Removes a listener that was previously added with addListener(). */ void removeListener (Listener* listener); + /** This method uses a comparator object to sort the tree's children into order. + + The object provided must have a method of the form: + @code + int compareElements (const ValueTree& first, const ValueTree& 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 sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) { @@ -6914,6 +13137,9 @@ public: } } + /** An invalid ValueTree that can be used if you need to return one as an error condition, etc. + This invalid object is equivalent to ValueTree created with its default constructor. + */ static const ValueTree invalid; juce_UseDebuggingNewOperator @@ -6991,6 +13217,7 @@ private: ListenerList listeners; public: + /** @internal */ explicit ValueTree (SharedObject*); // (can be made private when VC6 support is finally dropped) }; @@ -7008,6 +13235,12 @@ public: #ifndef __JUCE_VOIDARRAY_JUCEHEADER__ #define __JUCE_VOIDARRAY_JUCEHEADER__ +/** + 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__ @@ -7027,20 +13260,61 @@ typedef Array VoidArray; #ifndef __JUCE_FILELOGGER_JUCEHEADER__ #define __JUCE_FILELOGGER_JUCEHEADER__ +/** + 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); const File getLogFile() const { return logFile; } + /** 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, @@ -7070,27 +13344,94 @@ private: #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(); +/** A utility object that helps you initialise and shutdown Juce correctly + using an RAII pattern. + + When an instance of this class is created, it calls initialiseJuce_NonGUI(), + and when it's deleted, it calls shutdownJuce_NonGUI(), which lets you easily + make sure that these functions are matched correctly. + + This class is particularly handy to use at the beginning of a console app's + main() function, because it'll take care of shutting down whenever you return + from the main() call. + + @see ScopedJuceInitialiser_GUI +*/ class ScopedJuceInitialiser_NonGUI { public: + /** The constructor simply calls initialiseJuce_NonGUI(). */ ScopedJuceInitialiser_NonGUI() { initialiseJuce_NonGUI(); } + /** The destructor simply calls shutdownJuce_NonGUI(). */ ~ScopedJuceInitialiser_NonGUI() { shutdownJuce_NonGUI(); } }; +/** A utility object that helps you initialise and shutdown Juce correctly + using an RAII pattern. + + When an instance of this class is created, it calls initialiseJuce_GUI(), + and when it's deleted, it calls shutdownJuce_GUI(), which lets you easily + make sure that these functions are matched correctly. + + This class is particularly handy to use at the beginning of a console app's + main() function, because it'll take care of shutting down whenever you return + from the main() call. + + @see ScopedJuceInitialiser_NonGUI +*/ class ScopedJuceInitialiser_GUI { public: + /** The constructor simply calls initialiseJuce_GUI(). */ ScopedJuceInitialiser_GUI() { initialiseJuce_GUI(); } + /** The destructor simply calls shutdownJuce_GUI(). */ ~ScopedJuceInitialiser_GUI() { shutdownJuce_GUI(); } }; @@ -7114,20 +13455,66 @@ public: #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 @@ -7155,14 +13542,22 @@ private: #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(); + /** Tries to launch the system's default reader for a given file or URL. */ static bool openDocument (const String& documentURL, const String& parameters); + /** Tries to launch the system's default email app to let the user create an email. + */ static bool launchEmailWithAttachments (const String& targetEmailAddress, const String& emailSubject, const String& bodyText, @@ -7170,22 +13565,35 @@ public: #if JUCE_MAC || JUCE_IPHONE || DOXYGEN + /** 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 - 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); + /** MAC ONLY - Adds an item to the dock */ static void addItemToDock (const File& file); + /** MAC ONLY - Returns the current OS version number. + E.g. if it's running on 10.4, this will be 4, 10.5 will return 5, etc. + */ static int getOSXMinorVersionNumber(); #endif @@ -7193,39 +13601,105 @@ public: // 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 - Creates a file association in the registry. + + This lets you set the exe that should be launched by a given file extension. + @param fileExtension the file extension to associate, including the + initial dot, e.g. ".txt" + @param symbolicDescription a space-free short token to identify the file type + @param fullDescription a human-readable description of the file type + @param targetExecutable the executable that should be launched + @param iconResourceNumber the icon that gets displayed for the file type will be + found by looking up this resource number in the + executable. Pass 0 here to not use an icon + */ static void registerFileAssociation (const String& fileExtension, const String& symbolicDescription, const String& fullDescription, const File& targetExecutable, int iconResourceNumber); + /** 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(); + /** WIN32 ONLY - Gets the command-line params as a string. + + This is needed to avoid unicode problems with the argc type params. + */ static const String JUCE_CALLTYPE getCurrentCommandLineParams() throw(); #endif + /** Clears the floating point unit's flags. + + Only has an effect under win32, currently. + */ static void fpuReset(); #if JUCE_LINUX || JUCE_WINDOWS + /** 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 @@ -7242,6 +13716,9 @@ private: #if JUCE_MAC || JUCE_IPHONE +/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object + using RAII. +*/ class ScopedAutoReleasePool { public: @@ -7259,11 +13736,20 @@ private: #if JUCE_LINUX +/** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server + using an RAII approach. +*/ class ScopedXLock { public: + /** Creating a ScopedXLock object locks the X display. + This uses XLockDisplay() to grab the display that Juce is using. + */ ScopedXLock(); + /** Deleting a ScopedXLock object unlocks the X display. + This calls XUnlockDisplay() to release the lock. + */ ~ScopedXLock(); }; @@ -7271,6 +13757,12 @@ public: #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: @@ -7278,6 +13770,9 @@ 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). */ @@ -7293,18 +13788,43 @@ public: 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); + /** Stops the device running. + @see start + */ void stop(); + /** Returns true if the device has been started successfully. + */ bool isActive() const; + /** Returns the ID number of the remote, if it has sent one. + */ int getRemoteId() const { return remoteId; } juce_UseDebuggingNewOperator + /** @internal */ void handleCallbackInternal(); private: @@ -7331,36 +13851,92 @@ private: #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()) + */ explicit Random (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 (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(); + /** Returns a BigInteger containing a random number. + + @returns a random value in the range 0 to (maximumValue - 1). + */ const BigInteger nextLargeNumber (const BigInteger& maximumValue); + /** Sets a range of bits in a BigInteger to random values. */ void fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits); + /** 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 (int64 newSeed) throw(); + /** Merges this object's seed with another value. + This sets the seed to be a value created by combining the current seed and this + new value. + */ void combineSeed (int64 seedValue) throw(); + /** Reseeds this generator using a value generated from various semi-random system + properties like the current time, etc. + + Because this function convolves the time with the last seed value, calling + it repeatedly will increase the randomness of the final result. + */ void setSeedRandomly(); juce_UseDebuggingNewOperator @@ -7383,6 +13959,63 @@ private: #ifndef __JUCE_SINGLETON_JUCEHEADER__ #define __JUCE_SINGLETON_JUCEHEADER__ +/** + Macro to declare member variables and methods for a singleton class. + + To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) + to the class's definition. + + Then put a macro juce_ImplementSingleton (MyClass) along with the class's + implementation code. + + It's also a very good idea to also add the call clearSingletonInstance() in your class's + destructor, in case it is deleted by other means than deleteInstance() + + Clients can then call the static method MyClass::getInstance() to get a pointer + to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if + no instance currently exists. + + 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 doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + + 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, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ @@ -7438,11 +14071,35 @@ private: _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. + + If doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + + 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, doNotRecreateAfterDeletion) \ \ static classname* _singletonInstance; \ @@ -7491,6 +14148,23 @@ private: _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; \ @@ -7524,6 +14198,11 @@ private: _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; @@ -7542,12 +14221,23 @@ private: #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, @@ -7570,38 +14260,104 @@ public: 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 OperatingSystemType 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(); + /** Returns the current user's name, if available. + @see getFullUserName() + */ static const String getLogonName(); + /** Returns the current user's full name, if available. + On some OSes, this may just return the same value as getLogonName(). + @see getLogonName() + */ static const String getFullUserName(); // 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. If this is true, + the least-significant byte of each number is the first byte + of the mac address. If false, the least significant byte is + the last number. 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 bool littleEndian = true); @@ -7609,6 +14365,10 @@ public: bool littleEndian = false); #endif + /** Returns a list of MAC addresses found on this machine. + + @returns an array of strings containing the MAC addresses that were found + */ static const StringArray getMACAddressStrings(); // not-for-public-use platform-specific method gets called at startup to initialise things. @@ -7637,34 +14397,75 @@ private: #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 @@ -7690,20 +14491,33 @@ private: #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. + + The key data can be up to 72 bytes long. + */ BlowFish (const void* keyData, int keyBytes); + /** Creates a copy of another blowfish object. */ BlowFish (const BlowFish& other); + /** Copies another blowfish object. */ BlowFish& operator= (const BlowFish& other); + /** Destructor. */ ~BlowFish(); + /** Encrypts a pair of 32-bit integers. */ void encrypt (uint32& data1, uint32& data2) const throw(); + /** Decrypts a pair of 32-bit integers. */ void decrypt (uint32& data1, uint32& data2) const throw(); juce_UseDebuggingNewOperator @@ -7726,34 +14540,67 @@ private: #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. */ MD5& operator= (const MD5& other); + /** Creates a checksum for a block of binary data. */ explicit MD5 (const MemoryBlock& data); + /** Creates a checksum for a block of binary data. */ MD5 (const void* data, const size_t numBytes); + /** Creates a checksum for a string. + + Note that this operates on the string as a block of unicode characters, so the + result you get will differ from the value you'd get if the string was treated + as a block of utf8 or ascii. Bear this in mind if you're comparing the result + of this method with a checksum created by a different framework, which may have + used a different encoding. + */ explicit 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, int64 numBytesToRead = -1); + /** Creates a checksum for a file. */ explicit 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 @@ -7788,15 +14635,39 @@ private: #ifndef __JUCE_PRIMES_JUCEHEADER__ #define __JUCE_PRIMES_JUCEHEADER__ +/** + Prime number creation class. + + This class contains static methods for generating and testing prime numbers. + + @see BigInteger +*/ 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. + + The randomSeeds parameter lets you optionally pass it a set of values with + which to seed the random number generation, improving the security of the + keys generated. + */ static const BigInteger createProbablePrime (int bitLength, int certainty, const int* randomSeeds = 0, int numRandomSeeds = 0); + /** 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 BigInteger& number, int certainty); private: @@ -7816,20 +14687,64 @@ private: #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(); + /** Loads a key from an encoded string representation. + + This reloads a key from a string created by the toString() method. + */ explicit RSAKey (const String& stringRepresentation); + /** Destructor. */ ~RSAKey(); + /** Turns the key into a string representation. + + This can be reloaded using the constructor that takes a string. + */ const String toString() const; + /** 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 couldn't be completed, e.g. if this key hasn't been + initialised correctly. + + NOTE: This method dumbly applies this key to this data. If you encode some data + and then try to decode it with a key that doesn't match, this method will still + happily do its job and return true, but the result won't be what you were expecting. + It's your responsibility to check that the result is what you wanted. + */ bool applyToValue (BigInteger& value) const; + /** 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. + + The randomSeeds parameter lets you optionally pass it a set of values with + which to seed the random number generation, improving the security of the + keys generated. + */ static void createKeyPair (RSAKey& publicKey, RSAKey& privateKey, int numBits, @@ -7853,24 +14768,84 @@ protected: #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 = "*", int whatToLookFor = File::findFiles); + /** Destructor. */ ~DirectoryIterator(); + /** Moves 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(); + /** Moves the iterator along to the next file, and returns various properties of that file. + + If you need to find out details about the file, it's more efficient to call this method than + to call the normal next() method and then find out the details afterwards. + + All the parameters are optional, so pass null pointers for any items that you're not + interested in. + + @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. If it returns false, then none of the + parameters will be filled-in. + */ bool next (bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly); + /** 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; + /** 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; juce_UseDebuggingNewOperator @@ -7929,12 +14904,23 @@ private: #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 + */ explicit FileInputStream (const File& fileToRead); + /** Destructor. */ ~FileInputStream(); const File& getFile() const throw() { return file; } @@ -7968,17 +14954,43 @@ private: #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. + + @see TemporaryFile + */ FileOutputStream (const File& fileToWriteTo, int bufferSizeToUse = 16384); + /** Destructor. */ ~FileOutputStream(); + /** Returns the file that this stream is writing to. + */ const File& getFile() const { return file; } + /** Returns true if the stream couldn't be opened for some reason. + */ bool failedToOpen() const { return fileHandle == 0; } void flush(); @@ -8013,44 +15025,122 @@ private: #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" + */ 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[] (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, 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 (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 (Array& results, int whatToLookFor, bool searchRecursively, const String& wildCardPattern = "*") 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, bool checkRecursively) const; @@ -8073,29 +15163,68 @@ private: #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; + /** Returns the last name that was used to try to open this pipe. */ const String getName() const; + /** 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 @@ -8122,6 +15251,44 @@ private: #ifndef __JUCE_TEMPORARYFILE_JUCEHEADER__ #define __JUCE_TEMPORARYFILE_JUCEHEADER__ +/** + Manages a temporary file, which will be deleted when this object is deleted. + + This object is intended to be used as a stack based object, using its scope + to make sure the temporary file isn't left lying around. + + For example: + + @code + { + File myTargetFile ("~/myfile.txt"); + + // this will choose a file called something like "~/myfile_temp239348.txt" + // which definitely doesn't exist at the time the constructor is called. + TemporaryFile temp (myTargetFile); + + // create a stream to the temporary file, and write some data to it... + ScopedPointer out (temp.getFile().createOutputStream()); + + if (out != 0) + { + out->write ( ...etc ) + out->flush(); + out = 0; // (deletes the stream) + + // ..now we've finished writing, this will rename the temp file to + // make it replace the target file we specified above. + bool succeeded = temp.overwriteTargetFileWithTemporary(); + } + + // ..and even if something went wrong and our overwrite failed, + // as the TemporaryFile object goes out of scope here, it'll make sure + // that the temp file gets deleted. + } + @endcode + + @see File, FileOutputStream +*/ class JUCE_API TemporaryFile { public: @@ -8135,18 +15302,64 @@ public: than just being appended (see File::getNonexistentSibling() )*/ }; + /** Creates a randomly-named temporary file in the default temp directory. + + @param suffix a file suffix to use for the file + @param optionFlags a combination of the values listed in the OptionFlags enum + The file will not be created until you write to it. And remember that when + this object is deleted, the file will also be deleted! + */ TemporaryFile (const String& suffix = String::empty, int optionFlags = 0); + /** Creates a temporary file in the same directory as a specified file. + + This is useful if you have a file that you want to overwrite, but don't + want to harm the original file if the write operation fails. You can + use this to create a temporary file next to the target file, then + write to the temporary file, and finally use overwriteTargetFileWithTemporary() + to replace the target file with the one you've just written. + + This class won't create any files until you actually write to them. And remember + that when this object is deleted, the temporary file will also be deleted! + + @param targetFile the file that you intend to overwrite - the temporary + file will be created in the same directory as this + @param optionFlags a combination of the values listed in the OptionFlags enum + */ TemporaryFile (const File& targetFile, int optionFlags = 0); + /** Destructor. + + When this object is deleted it will make sure that its temporary file is + also deleted! If the operation fails, it'll throw an assertion in debug + mode. + */ ~TemporaryFile(); + /** Returns the temporary file. */ const File getFile() const { return temporaryFile; } + /** Returns the target file that was specified in the constructor. */ const File getTargetFile() const { return targetFile; } + /** Tries to move the temporary file to overwrite the target file that was + specified in the constructor. + + If you used the constructor that specified a target file, this will attempt + to replace that file with the temporary one. + + Before calling this, make sure: + - that you've actually written to the temporary file + - that you've closed any open streams that you were using to write to it + - and that you don't have any streams open to the target file, which would + prevent it being overwritten + + If the file move succeeds, this returns false, and the temporary file will + have disappeared. If it fails, the temporary file will probably still exist, + but will be deleted when this object is destroyed. + */ bool overwriteTargetFileWithTemporary() const; juce_UseDebuggingNewOperator @@ -8177,18 +15390,40 @@ private: #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 @@ -8197,40 +15432,106 @@ public: #endif // __JUCE_INPUTSOURCE_JUCEHEADER__ /*** End of inlined file: juce_InputSource.h ***/ +/** + 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* inputStream, bool deleteStreamWhenDestroyed) throw(); + /** Creates a ZipFile based for a file. */ ZipFile (const File& file); + /** Creates a ZipFile for an input source. + + The inputSource object will be owned by the zip file, which will delete + it later when not needed. + */ ZipFile (InputSource* inputSource); + /** 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 (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 (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, bool shouldOverwriteFiles = true); @@ -8273,40 +15574,126 @@ private: #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 (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, int remotePortNumber, 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 (bool readyForReading, int timeoutMsecs) const; + /** Reads bytes from the socket. + + If blockUntilSpecifiedAmountHasArrived is true, the method will block until + maxBytesToRead bytes have been read, (or until an error occurs). If this + flag is false, the method will return as much data as is currently available + without blocking. + + @returns the number of bytes read, or -1 if there was an error. + @see waitUntilReady + */ int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived); + /** 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, 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. + + @param portNumber the port number to listen on + @param localHostName the interface address to listen on - pass an empty + string to listen on all addresses + @returns true if it manages to open the socket successfully. + + @see waitForNextConnection + */ bool createListener (int portNumber, const String& localHostName = String::empty); + /** 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 @@ -8321,39 +15708,115 @@ private: 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(). + + If enableBroadcasting is true, the socket will be allowed to send broadcast messages + (may require extra privileges on linux) + + To wait for other sockets to connect to this one, call waitForNextConnection(). + */ DatagramSocket (int localPortNumber, bool enableBroadcasting = false); + /** 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 (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, int remotePortNumber, 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 (bool readyForReading, int timeoutMsecs) const; + /** Reads bytes from the socket. + + If blockUntilSpecifiedAmountHasArrived is true, the method will block until + maxBytesToRead bytes have been read, (or until an error occurs). If this + flag is false, the method will return as much data as is currently available + without blocking. + + @returns the number of bytes read, or -1 if there was an error. + @see waitUntilReady + */ int read (void* destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived); + /** 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, 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 @@ -8380,73 +15843,245 @@ private: #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(); + /** Creates a URL from a string. */ URL (const String& url); + /** Creates a copy of another URL. */ URL (const URL& other); + /** Destructor. */ ~URL(); + /** Copies this URL from another one. */ 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 (bool includeGetParameters) const; + /** True if it seems to be valid. */ bool isWellFormed() const; + /** Returns just the domain part of the URL. + + E.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com". + */ const String getDomain() const; + /** Returns the path part of the URL. + + E.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar". + */ const String getSubPath() const; + /** Returns the scheme of the URL. + + E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't + include the colon). + */ const String getScheme() const; + /** Returns a new version of this URL that uses a different sub-path. + + E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with + "bar", it'll return "http://www.xyz.com/bar?x=1". + */ const URL withNewSubPath (const String& newPath) 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; + /** 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; + /** Returns the set of mime types associated with each of the upload files. + */ const StringPairArray& getMimeTypesOfUploadFiles() const; + /** Returns a copy of this URL, with a block of data to send as the POST data. + + If you're setting the POST data, be careful not to have any parameters set + as well, otherwise it'll all get thrown in together, and might not have the + desired effect. + + If the URL already contains some POST data, this will replace it, rather + than being appended to it. + + This data will only be used if you specify a post operation when you call + createInputStream(). + */ const URL withPOSTData (const String& postData) const; + /** Returns the data that was set using withPOSTData(). + */ const String getPostData() const { return postData; } + /** 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 + @param extraHeaders if not empty, this string is appended onto the headers that + are used for the request. It must therefore be a valid set of HTML + header directives, separated by newlines. + @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If + a negative number, it will be infinite. Otherwise it specifies a + time in milliseconds. + */ InputStream* createInputStream (bool usePostCommand, OpenStreamProgressCallback* progressCallback = 0, void* progressCallbackContext = 0, const String& extraHeaders = String::empty, int connectionTimeOutMs = 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, 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 (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 (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(). + + If isParameter is true, it means that the string is going to be used + as a parameter, so it also encodes '$' and ',' (which would otherwise + be legal in a URL. + + @see removeEscapeChars + */ static const String addEscapeChars (const String& stringToAddEscapeCharsTo, bool isParameter); + /** 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 @@ -8467,14 +16102,33 @@ private: #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* sourceStream, int bufferSize, bool deleteSourceWhenDestroyed); + /** Destructor. + + This may also delete the source stream, if that option was chosen when the + buffered stream was created. + */ ~BufferedInputStream(); int64 getTotalLength(); @@ -8509,6 +16163,11 @@ private: #ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ #define __JUCE_FILEINPUTSOURCE_JUCEHEADER__ +/** + A type of InputSource that represents a normal file. + + @see InputSource +*/ class JUCE_API FileInputSource : public InputSource { public: @@ -8542,15 +16201,34 @@ private: class GZIPCompressorHelper; +/** + 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* destStream, int compressionLevel = 0, bool deleteDestStreamWhenDestroyed = false, bool noWrap = false); + /** Destructor. */ ~GZIPCompressorOutputStream(); void flush(); @@ -8584,15 +16262,36 @@ private: class GZIPDecompressHelper; +/** + 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* sourceStream, bool deleteSourceWhenDestroyed, bool noWrap = false, int64 uncompressedStreamLength = -1); + /** Destructor. */ ~GZIPDecompressorInputStream(); int64 getPosition(); @@ -8635,17 +16334,43 @@ private: #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* sourceData, size_t sourceDataSize, bool keepInternalCopyOfData); + /** Creates a MemoryInputStream. + + @param data a block of data to use as the stream's source + @param keepInternalCopyOfData if false, the stream will just keep a reference 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 MemoryBlock& data, bool keepInternalCopyOfData); + /** Destructor. */ ~MemoryInputStream(); int64 getPosition(); @@ -8676,22 +16401,50 @@ private: #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 (size_t initialSize = 256, size_t granularity = 256, MemoryBlock* memoryBlockToWriteTo = 0); + /** Destructor. + + This will free any data that was written to it. + */ ~MemoryOutputStream(); + /** Returns a pointer to the data that has been written to the stream. + + @see getDataSize + */ const char* getData() const throw(); + /** Returns the number of bytes of data that have been written to the stream. + + @see getData + */ size_t getDataSize() const throw() { return size; } + /** Resets the stream, clearing any data that has been written to it so far. */ void reset() throw(); + /** Returns a String created from the (UTF8) data that has been written to the stream. */ const String toUTF8() const; void flush(); @@ -8724,15 +16477,42 @@ private: #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 cause an 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* sourceStream, int64 startPositionInSourceStream, int64 lengthOfSourceStream, 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(); @@ -8766,33 +16546,147 @@ private: #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); + /** 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); + /** Destructor. */ ~LocalisedStrings(); + /** 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); + /** 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(); + /** 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); + /** 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); + /** 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; + /** 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 { 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 { return countryCodes; } + /** Indicates whether to use a case-insensitive search when looking up a string. + This defaults to true. + */ void setIgnoresCase (const bool shouldIgnoreCase); juce_UseDebuggingNewOperator @@ -8825,22 +16719,96 @@ private: #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); + /** 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(); + /** 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(); + /** Sets a flag to change the treatment of empty text elements. + + If this is true (the default state), then any text elements that contain only + whitespace characters will be ingored during parsing. If you need to catch + whitespace-only text, then you should set this to false before calling the + getDocumentElement() method. + */ void setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw(); juce_UseDebuggingNewOperator @@ -8894,26 +16862,79 @@ private: #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 + */ explicit InterProcessLock (const String& name); + /** Destructor. + + This will also release the lock if it's currently held by this process. + */ ~InterProcessLock(); + /** 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); + /** Releases the lock if it's currently held by this process. + */ void exit(); + /** + Automatically locks and unlocks an InterProcessLock object. + + This works like a ScopedLock, but using an InterprocessLock rather than + a CriticalSection. + + @see ScopedLock + */ class ScopedLockType { public: + /** Creates a scoped lock. + + As soon as it is created, this will lock the InterProcessLock, and + when the ScopedLockType object is deleted, the InterProcessLock will + be unlocked. + + Note that since an InterprocessLock can fail due to errors, you should check + isLocked() to make sure that the lock was successful before using it. + + 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. + */ explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } + /** Destructor. + + The InterProcessLock 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 ~ScopedLockType() { lock_.exit(); } + /** Returns true if the InterProcessLock was successfully locked. */ bool isLocked() const throw() { return lockWasSuccessful; } private: @@ -8951,6 +16972,13 @@ private: #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: @@ -8963,16 +16991,44 @@ public: 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(); + /** 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(); private: @@ -8997,18 +17053,61 @@ private: #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. + + @param manualReset If this is false, the event will be reset automatically when the wait() + method is called. If manualReset is true, then once the event is signalled, + the only way to reset it will be by calling the reset() method. + */ WaitableEvent (bool manualReset = false) 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 if manualReset + was set to false in the WaitableEvent's constructor, then the event will be reset. + + @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 (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 @@ -9028,61 +17127,234 @@ private: #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. + */ explicit 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(); + /** 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 (int priority); + /** 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 (int timeOutMilliseconds); + /** Returns true if the thread is currently active */ bool isThreadRunning() const; + /** 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. + + If your thread makes use of wait(), you might want to call notify() after calling + this method, to interrupt any waits that might be in progress, and allow it + to reach a point where it can exit. + + @see threadShouldExit + @see waitForThreadToExit + */ void signalThreadShouldExit(); + /** 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 { 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 (int timeOutMilliseconds) const; + /** Changes the thread's priority. + May return false if for some reason the priority can't be changed. + + @param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority + of 5 is normal. + */ bool setPriority (int priority); + /** Changes the priority of the caller thread. + + Similar to setPriority(), but this static method acts on the caller thread. + May return false if for some reason the priority can't be changed. + + @see setPriority + */ static bool setCurrentThreadPriority (int priority); + /** 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 (uint32 affinityMask); + /** 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 (uint32 affinityMask); // this can be called from any thread that needs to pause.. static void JUCE_CALLTYPE sleep (int milliseconds); + /** Yields the calling thread's current time-slot. */ static void JUCE_CALLTYPE yield(); + /** 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. + + A negative time-out value means that the method will wait indefinitely. + + @returns true if the event has been signalled, false if the timeout expires. + */ bool wait (int timeOutMilliseconds) const; + /** Wakes up the thread. + + If the thread has called the wait() method, this will wake it up. + + @see wait + */ void notify() const; + /** A value type used for thread IDs. + @see getCurrentThreadId(), getThreadId() + */ typedef void* ThreadID; + /** 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 ThreadID getCurrentThreadId(); + /** 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(); + /** 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 + */ ThreadID getThreadId() const throw() { return threadId_; } + /** Returns the name of the thread. + + This is the name that gets set in the constructor. + */ const String getThreadName() const { 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(); + /** 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 (int timeoutInMillisecs); juce_UseDebuggingNewOperator @@ -9110,22 +17382,90 @@ private: #endif // __JUCE_THREAD_JUCEHEADER__ /*** End of inlined file: juce_Thread.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(); + /** Tries to lock this object for writing. + + This is like enterWrite(), but doesn't block - it returns true if it manages + to obtain the lock. + + @see enterWrite + */ bool tryEnterWrite() 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 @@ -9156,12 +17496,51 @@ private: #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 explicit 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: @@ -9183,14 +17562,63 @@ private: #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 explicit 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(); } + /** Returns true if the CriticalSection was successfully locked. */ bool isLocked() const throw() { return lockWasSuccessful; } private: @@ -9213,12 +17641,51 @@ private: #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 explicit 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: @@ -9246,18 +17713,44 @@ private: 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(). + */ explicit 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 @@ -9270,12 +17763,39 @@ public: 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 { 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 { 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 @@ -9291,48 +17811,154 @@ private: ThreadPoolJob& operator= (const ThreadPoolJob&); }; +/** + 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 (int numberOfThreads, bool startThreadsOnlyWhenNeeded = true, 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(); + /** A callback class used when you need to select which ThreadPoolJob objects are suitable + for some kind of operation. + @see ThreadPool::removeAllJobs + */ class JUCE_API JobSelector { public: virtual ~JobSelector() {} + /** Should return true if the specified thread matches your criteria for whatever + operation that this object is being used for. + + Any implementation of this method must be extremely fast and thread-safe! + */ virtual bool isJobSuitable (ThreadPoolJob* job) = 0; }; + /** 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* 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* job, bool interruptIfRunning, int timeOutMilliseconds); + /** Tries to remove 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 + @param deleteInactiveJobs if true, any jobs that aren't currently running will be deleted. If false, + they will simply be removed from the pool. Jobs that are already running when + this method is called can choose whether they should be deleted by + returning jobHasFinishedAndShouldBeDeleted from their runJob() method. + @param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which + jobs should be removed. If it is zero, all jobs are removed + @returns true if all jobs are successfully stopped and removed; false if the timeout period + expires while waiting for one or more jobs to stop + */ bool removeAllJobs (bool interruptRunningJobs, int timeOutMilliseconds, bool deleteInactiveJobs = false, JobSelector* selectedJobsToRemove = 0); + /** Returns the number of jobs currently running or queued. + */ int getNumJobs() const; + /** 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 (int index) const; + /** Returns true if the given job is currently queued or running. + + @see isJobRunning() + */ bool contains (const ThreadPoolJob* job) const; + /** Returns true if the given job is currently being run by a thread. + */ bool isJobRunning (const ThreadPoolJob* 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* job, 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 (bool onlyReturnActiveJobs) const; + /** Changes the priority of all the threads. + + This will call Thread::setPriority() for each thread in the pool. + May return false if for some reason the priority can't be changed. + */ bool setThreadPriorities (int newPriority); juce_UseDebuggingNewOperator @@ -9366,30 +17992,88 @@ private: #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. + */ explicit 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* 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* client); + /** Returns the number of registered clients. */ int getNumClients() const; + /** Returns one of the registered clients. */ TimeSliceClient* getClient (int index) const; + /** @internal */ void run(); juce_UseDebuggingNewOperator @@ -9451,10 +18135,17 @@ class Image; 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. */ @@ -9483,24 +18174,68 @@ public: BottomRightCornerResizeCursor /**< A platform-specific cursor for resizing the bottom-right-corner of a window. */ }; + /** Creates the standard arrow cursor. */ MouseCursor(); + /** Creates one of the standard mouse cursor */ MouseCursor (StandardCursorType type); + /** 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 (const Image& image, int hotSpotX, int hotSpotY); + /** Creates a copy of another cursor object. */ MouseCursor (const MouseCursor& other); + /** Copies this cursor from another object. */ MouseCursor& operator= (const MouseCursor& other); + /** Destructor. */ ~MouseCursor(); + /** 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(); + /** 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(); juce_UseDebuggingNewOperator @@ -9530,25 +18265,134 @@ private: class MouseEvent; +/** + 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); @@ -9569,83 +18413,165 @@ class MouseInputSource; #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 (int flags = 0) throw(); + /** Creates a copy of another object. */ ModifierKeys (const ModifierKeys& other) throw(); + /** Copies this object from another one. */ 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 a copy of only the mouse-button flags */ const ModifierKeys withOnlyMouseButtons() const throw() { return ModifierKeys (flags & allMouseButtonModifiers); } + /** Returns a copy of only the non-mouse flags */ const ModifierKeys withoutMouseButtons() const throw() { return ModifierKeys (flags & ~allMouseButtonModifiers); } bool operator== (const ModifierKeys& other) const throw() { return flags == other.flags; } bool operator!= (const ModifierKeys& other) const throw() { return flags != other.flags; } + /** Returns the raw flags for direct testing. */ inline int getRawFlags() const throw() { return flags; } inline const ModifierKeys withoutFlags (int rawFlagsToClear) const throw() { return ModifierKeys (flags & ~rawFlagsToClear); } inline const ModifierKeys withFlags (int rawFlagsToSet) const throw() { return ModifierKeys (flags | rawFlagsToSet); } + /** 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; } + /** Returns the total number of mouse buttons that are down. */ int getNumMouseButtonsDown() const throw(); + /** 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: @@ -9674,74 +18600,153 @@ private: #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 (float mat00, float mat01, float mat02, float mat10, float mat11, float mat12) throw(); + /** Copies from another AffineTransform object */ 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 (float deltaX, float deltaY) const throw(); + /** Returns a new transform which is a translation. */ static const AffineTransform translation (float deltaX, 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 (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 (float angleInRadians, float pivotX, float pivotY) const throw(); + /** Returns a new transform which is a rotation about (0, 0). */ static const AffineTransform rotation (float angleInRadians) throw(); + /** Returns a new transform which is a rotation about a given point. */ static const AffineTransform rotation (float angleInRadians, float pivotX, 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 (float factorX, float factorY) const throw(); + /** Returns a new transform which is a re-scale about the origin. */ static const AffineTransform scale (float factorX, 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 (float shearX, 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(); + /** Returns true if the transform only translates, and doesn't scale or rotate the + points. */ bool isOnlyTranslation() const throw(); + /** If this transform is only a translation, this returns the X offset. + @see isOnlyTranslation + */ float getTranslationX() const throw() { return mat02; } + /** If this transform is only a translation, this returns the X offset. + @see isOnlyTranslation + */ float getTranslationY() const throw() { return mat12; } juce_UseDebuggingNewOperator + /* The transform matrix is: + + (mat00 mat01 mat02) + (mat10 mat11 mat12) + ( 0 0 1 ) + */ float mat00, mat01, mat02; float mat10, mat11, mat12; @@ -9754,64 +18759,105 @@ private: #endif // __JUCE_AFFINETRANSFORM_JUCEHEADER__ /*** End of inlined file: juce_AffineTransform.h ***/ +/** + A pair of (x, y) co-ordinates. + + The ValueType template should be a primitive type such as int, float, double, + rather than a class. + + @see Line, Path, AffineTransform +*/ template class Point { public: + /** Creates a point with co-ordinates (0, 0). */ Point() throw() : x (0), y (0) {} + /** Creates a copy of another point. */ Point (const Point& other) throw() : x (other.x), y (other.y) {} + /** Creates a point from an (x, y) position. */ Point (const ValueType initialX, const ValueType initialY) throw() : x (initialX), y (initialY) {} + /** Destructor. */ ~Point() throw() {} + /** Copies this point from another one. */ Point& operator= (const Point& other) throw() { x = other.x; y = other.y; return *this; } + /** Returns the point's x co-ordinate. */ inline ValueType getX() const throw() { return x; } + /** Returns the point's y co-ordinate. */ inline ValueType getY() const throw() { return y; } inline bool operator== (const Point& other) const throw() { return x == other.x && y == other.y; } inline bool operator!= (const Point& other) const throw() { return x != other.x || y != other.y; } + /** Returns true if the point is (0, 0). */ bool isOrigin() const throw() { return x == ValueType() && y == ValueType(); } + /** Returns a point which has the same Y position as this one, but a new X. */ const Point withX (const ValueType newX) const throw() { return Point (newX, y); } + /** Returns a point which has the same X position as this one, but a new Y. */ const Point withY (const ValueType newY) const throw() { return Point (x, newY); } + /** Changes the point's x and y co-ordinates. */ void setXY (const ValueType newX, const ValueType newY) throw() { x = newX; y = newY; } + /** Adds a pair of co-ordinates to this value. */ void addXY (const ValueType xToAdd, const ValueType yToAdd) throw() { x += xToAdd; y += yToAdd; } + /** Adds two points together. */ const Point operator+ (const Point& other) const throw() { return Point (x + other.x, y + other.y); } + /** Adds another point's co-ordinates to this one. */ Point& operator+= (const Point& other) throw() { x += other.x; y += other.y; return *this; } + /** Subtracts one points from another. */ const Point operator- (const Point& other) const throw() { return Point (x - other.x, y - other.y); } + /** Subtracts another point's co-ordinates to this one. */ Point& operator-= (const Point& other) throw() { x -= other.x; y -= other.y; return *this; } + /** Returns a point whose coordinates are multiplied by a given value. */ const Point operator* (const ValueType multiplier) const throw() { return Point (x * multiplier, y * multiplier); } + /** Multiplies the point's co-ordinates by a value. */ Point& operator*= (const ValueType multiplier) throw() { x *= multiplier; y *= multiplier; return *this; } + /** Returns a point whose coordinates are divided by a given value. */ const Point operator/ (const ValueType divisor) const throw() { return Point (x / divisor, y / divisor); } + /** Divides the point's co-ordinates by a value. */ Point& operator/= (const ValueType divisor) throw() { x /= divisor; y /= divisor; return *this; } + /** Returns the inverse of this point. */ const Point operator-() const throw() { return Point (-x, -y); } + /** Returns the straight-line distance between this point and another one. */ ValueType getDistanceFromOrigin() const throw() { return (ValueType) juce_hypot (x, y); } + /** Returns the straight-line distance between this point and another one. */ ValueType getDistanceFrom (const Point& other) const throw() { return (ValueType) juce_hypot (x - other.x, y - other.y); } + /** Returns the angle from this point to another one. + + The return value is the number of radians clockwise from the 3 o'clock direction, + where this point is the centre and the other point is on the radius. + */ ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) atan2 (other.x - x, other.y - y); } + /** Uses a transform to change the point's co-ordinates. + This will only compile if ValueType = float! + @see AffineTransform::transformPoint + */ void applyTransform (const AffineTransform& transform) throw() { transform.transformPoint (x, y); } + /** Returns the point as a string in the form "x, y". */ const String toString() const { return String (x) + ", " + String (y); } juce_UseDebuggingNewOperator @@ -9823,10 +18869,34 @@ private: #endif // __JUCE_POINT_JUCEHEADER__ /*** End of inlined file: juce_Point.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 source the source that's invoking the event + @param position the 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 mouseDownPos the 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 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 (MouseInputSource& source, const Point& position, const ModifierKeys& modifiers, @@ -9837,62 +18907,236 @@ public: int numberOfClicks, 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). + */ const 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). + */ const 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. + */ const 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* const 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* const originalComponent; + /** The time that this mouse-event occurred. + */ const Time eventTime; + /** The source device that generated this event. + */ MouseInputSource& source; + /** 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 co-ordinates of the last place that a mouse was pressed. + + The co-ordinates are relative to the component specified in MouseEvent::component. + + @see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked + */ const Point getMouseDownPosition() 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 the difference between the mouse's current postion and where it was + when the button was last pressed. + + @see getDistanceFromDragStart + */ const Point getOffsetFromDragStart() 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(); + /** The position of the mouse when the event occurred. + + This position is relative to the top-left of the component to which the + event applies (as indicated by the MouseEvent::eventComponent field). + */ const Point getPosition() 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 getScreenPosition + */ int getScreenX() const; + /** 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 getScreenPosition + */ int getScreenY() const; + /** Returns the mouse position of this event, in global screen co-ordinates. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getMouseDownScreenPosition + */ const Point getScreenPosition() const; + /** 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 getMouseDownScreenPosition + */ int getMouseDownScreenX() const; + /** 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 getMouseDownScreenPosition + */ int getMouseDownScreenY() const; + /** Returns the co-ordinates at which the mouse button was last pressed. + + The co-ordinates are relative to the top-left of the main monitor. + + @see getScreenPosition + */ const Point getMouseDownScreenPosition() const; + /** 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* otherComponent) const throw(); + /** Creates a copy of this event with a different position. + All other members of the event object are the same, but the x and y are + replaced with these new values. + */ const MouseEvent withNewPosition (const Point& newPosition) 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 (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 @@ -9916,25 +19160,80 @@ private: 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); + /** Called when the component is in the process of being deleted. + + This callback is made from inside the destructor, so be very, very cautious + about what you do inside the callback. + + It will be called before the component has been removed from its parent, and + before any child components have been removed. + */ virtual void componentBeingDeleted (Component& component); }; @@ -9951,42 +19250,135 @@ public: #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 (int keyCode, const ModifierKeys& modifiers, juce_wchar textCharacter) throw(); + /** Creates a keypress with a keyCode but no modifiers or text character. + */ KeyPress (int keyCode) throw(); + /** Creates a copy of another KeyPress. */ KeyPress (const KeyPress& other) throw(); + /** Copies this KeyPress from another one. */ 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 (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); + /** 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; + /** 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; + /** 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); // Key codes @@ -10070,14 +19462,47 @@ private: 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 + @param isKeyDown true if a key is being pressed, false if one is being released + @see KeyPress, Component::keyStateChanged + */ virtual bool keyStateChanged (bool isKeyDown, Component* originatingComponent); }; @@ -10091,17 +19516,61 @@ public: 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); }; @@ -10138,25 +19607,42 @@ public: #ifndef __JUCE_LINE_JUCEHEADER__ #define __JUCE_LINE_JUCEHEADER__ +/** + Represents a line. + + This class contains a bunch of useful methods for various geometric + tasks. + + The ValueType template parameter should be a primitive type - float or double + are what it's designed for. Integer types will work in a basic way, but some methods + that perform mathematical operations may not compile, or they may not produce + sensible results. + + @see Point, Rectangle, Path, Graphics::drawLine +*/ template class 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() : start (other.start), end (other.end) { } + /** Creates a line based on the co-ordinates of its start and end points. */ Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) throw() : start (startX, startY), end (endX, endY) { } + /** Creates a line from its start and end points. */ Line (const Point& startPoint, const Point& endPoint) throw() : start (startPoint), @@ -10164,6 +19650,7 @@ public: { } + /** Copies a line from another one. */ Line& operator= (const Line& other) throw() { start = other.start; @@ -10171,56 +19658,110 @@ public: return *this; } + /** Destructor. */ ~Line() throw() {} + /** Returns the x co-ordinate of the line's start point. */ inline ValueType getStartX() const throw() { return start.getX(); } + /** Returns the y co-ordinate of the line's start point. */ inline ValueType getStartY() const throw() { return start.getY(); } + /** Returns the x co-ordinate of the line's end point. */ inline ValueType getEndX() const throw() { return end.getX(); } + /** Returns the y co-ordinate of the line's end point. */ inline ValueType getEndY() const throw() { return end.getY(); } + /** Returns the line's start point. */ inline const Point& getStart() const throw() { return start; } + /** Returns the line's end point. */ inline const Point& getEnd() const throw() { return end; } + /** Changes this line's start point */ void setStart (ValueType newStartX, ValueType newStartY) throw() { start.setXY (newStartX, newStartY); } + /** Changes this line's end point */ void setEnd (ValueType newEndX, ValueType newEndY) throw() { end.setXY (newEndX, newEndY); } + /** Changes this line's start point */ void setStart (const Point& newStart) throw() { start = newStart; } + /** Changes this line's end point */ void setEnd (const Point& newEnd) throw() { end = newEnd; } + /** Applies an affine transform to the line's start and end points. */ void applyTransform (const AffineTransform& transform) throw() { start.applyTransform (transform); end.applyTransform (transform); } + /** Returns the length of the line. */ ValueType getLength() const throw() { return start.getDistanceFrom (end); } + /** Returns true if the line's start and end x co-ordinates are the same. */ bool isVertical() const throw() { return start.getX() == end.getX(); } + /** Returns true if the line's start and end y co-ordinates are the same. */ bool isHorizontal() const throw() { return start.getY() == end.getY(); } + /** 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. + */ ValueType getAngle() const throw() { return start.getAngleToPoint (end); } + /** Compares two lines. */ bool operator== (const Line& other) const throw() { return start == other.start && end == other.end; } + /** Compares two lines. */ bool operator!= (const Line& other) const throw() { return start != other.start || end != other.end; } + /** Finds the intersection between two lines. + + @param line the other line + @param intersection the position 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. + @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, Point& intersection) const throw() { return findIntersection (start, end, line.start, line.end, intersection); } + /** 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 (ValueType distanceFromStart) const throw() { return start + (end - start) * (distanceFromStart / getLength()); } + /** 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 (ValueType distanceFromStart, ValueType perpendicularDistance) const throw() { @@ -10233,11 +19774,30 @@ public: start.getY() + (ValueType) ((delta.getY() * distanceFromStart + delta.getX() * perpendicularDistance) / length)); } + /** 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 (ValueType proportionOfLength) const throw() { return start + (end - start) * proportionOfLength; } + /** 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. + + @returns the point's distance from the line + @see getPositionAlongLineOfNearestPoint + */ ValueType getDistanceFromLine (const Point& point) const throw() { const Point delta (end - start); @@ -10256,6 +19816,16 @@ public: point.getDistanceFrom (end)); } + /** 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 + */ ValueType findNearestPointTo (const Point& point) const throw() { const Point delta (end - start); @@ -10267,6 +19837,12 @@ public: + (point.getY() - start.getY()) * delta.getY()) / length)); } + /** 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 Point& point) const throw() { return start.getX() != end.getX() @@ -10274,11 +19850,21 @@ public: * (point.getX() - start.getX())) / (end.getX() - start.getX()) + start.getY(); } + /** 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 (ValueType distanceToShortenBy) const throw() { return Line (getPointAlongLine (jmin (distanceToShortenBy, getLength())), end); } + /** 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 (ValueType distanceToShortenBy) const throw() { const ValueType length = getLength(); @@ -10359,22 +19945,33 @@ private: class RectangleList; +/** + Manages a rectangle and allows geometric operations to be performed on it. + + @see RectangleList, Path, Line, Point +*/ template class Rectangle { public: + /** Creates a rectangle of zero size. + + The default co-ordinates will be (0, 0, 0, 0). + */ Rectangle() throw() : x (0), y (0), w (0), h (0) { } + /** Creates a copy of another rectangle. */ Rectangle (const Rectangle& other) throw() : x (other.x), y (other.y), w (other.w), h (other.h) { } + /** Creates a rectangle with a given position and size. */ Rectangle (const ValueType initialX, const ValueType initialY, const ValueType width, const ValueType height) throw() : x (initialX), y (initialY), @@ -10382,11 +19979,13 @@ public: { } + /** Creates a rectangle with a given size, and a position of (0, 0). */ Rectangle (const ValueType width, const ValueType height) throw() : x (0), y (0), w (width), h (height) { } + /** Creates a Rectangle from the positions of two opposite corners. */ Rectangle (const Point& corner1, const Point& corner2) throw() : x (jmin (corner1.getX(), corner2.getX())), y (jmin (corner1.getY(), corner2.getY())), @@ -10404,84 +20003,126 @@ public: return *this; } + /** Destructor. */ ~Rectangle() throw() {} + /** Returns true if the rectangle's width and height are both zero or less */ bool isEmpty() const throw() { return w <= 0 || h <= 0; } + /** Returns the x co-ordinate of the rectangle's left-hand-side. */ inline ValueType getX() const throw() { return x; } + /** Returns the y co-ordinate of the rectangle's top edge. */ inline ValueType getY() const throw() { return y; } + /** Returns the width of the rectangle. */ inline ValueType getWidth() const throw() { return w; } + /** Returns the height of the rectangle. */ inline ValueType getHeight() const throw() { return h; } + /** Returns the x co-ordinate of the rectangle's right-hand-side. */ inline ValueType getRight() const throw() { return x + w; } + /** Returns the y co-ordinate of the rectangle's bottom edge. */ inline ValueType getBottom() const throw() { return y + h; } + /** Returns the x co-ordinate of the rectangle's centre. */ ValueType getCentreX() const throw() { return x + w / (ValueType) 2; } + /** Returns the y co-ordinate of the rectangle's centre. */ ValueType getCentreY() const throw() { return y + h / (ValueType) 2; } + /** Returns the centre point of the rectangle. */ const Point getCentre() const throw() { return Point (x + w / (ValueType) 2, y + h / (ValueType) 2); } + /** Returns the aspect ratio of the rectangle's width / height. + If widthOverHeight is true, it returns width / height; if widthOverHeight is false, + it returns height / width. */ ValueType getAspectRatio (const bool widthOverHeight = true) const throw() { return widthOverHeight ? w / h : h / w; } + /** Returns the rectangle's top-left position as a Point. */ const Point getPosition() const throw() { return Point (x, y); } + /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ void setPosition (const Point& newPos) throw() { x = newPos.getX(); y = newPos.getY(); } + /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ void setPosition (const ValueType newX, const ValueType newY) throw() { x = newX; y = newY; } + /** Returns a rectangle with the same size as this one, but a new position. */ const Rectangle withPosition (const Point& newPos) const throw() { return Rectangle (newPos.getX(), newPos.getY(), w, h); } + /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ void setSize (const ValueType newWidth, const ValueType newHeight) throw() { w = newWidth; h = newHeight; } + /** Returns a rectangle with the same position as this one, but a new size. */ const Rectangle withSize (const ValueType newWidth, const ValueType newHeight) const throw() { return Rectangle (x, y, newWidth, newHeight); } + /** Changes all the rectangle's co-ordinates. */ void setBounds (const ValueType newX, const ValueType newY, const ValueType newWidth, const ValueType newHeight) throw() { x = newX; y = newY; w = newWidth; h = newHeight; } + /** Changes the rectangle's width */ void setWidth (const ValueType newWidth) throw() { w = newWidth; } + /** Changes the rectangle's height */ void setHeight (const ValueType newHeight) throw() { h = newHeight; } + /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ const Rectangle withX (const ValueType newX) const throw() { return Rectangle (newX, y, w, h); } + /** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */ const Rectangle withY (const ValueType newY) const throw() { return Rectangle (x, newY, w, h); } + /** Returns a rectangle which has the same position and height as this one, but with a different width. */ const Rectangle withWidth (const ValueType newWidth) const throw() { return Rectangle (x, y, newWidth, h); } + /** Returns a rectangle which has the same position and width as this one, but with a different height. */ const Rectangle withHeight (const ValueType newHeight) const throw() { return Rectangle (x, y, w, newHeight); } + /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. + If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. + */ void setLeft (const ValueType newLeft) throw() { w = jmax (ValueType(), x + w - newLeft); x = newLeft; } + /** Moves the y position, adjusting the height so that the bottom edge remains in the same place. + If the y is moved to be below the current bottom edge, the height will be set to zero. + */ void setTop (const ValueType newTop) throw() { h = jmax (ValueType(), y + h - newTop); y = newTop; } + /** Adjusts the width so that the right-hand edge of the rectangle has this new value. + If the new right is below the current X value, the X will be pushed down to match it. + @see getRight + */ void setRight (const ValueType newRight) throw() { x = jmin (x, newRight); w = newRight - x; } + /** Adjusts the height so that the bottom edge of the rectangle has this new value. + If the new bottom is lower than the current Y value, the Y will be pushed down to match it. + @see getBottom + */ void setBottom (const ValueType newBottom) throw() { y = jmin (y, newBottom); h = newBottom - y; } + /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */ void translate (const ValueType deltaX, const ValueType deltaY) throw() { @@ -10489,34 +20130,44 @@ public: y += deltaY; } + /** Returns a rectangle which is the same as this one moved by a given amount. */ const Rectangle translated (const ValueType deltaX, const ValueType deltaY) const throw() { return Rectangle (x + deltaX, y + deltaY, w, h); } + /** Returns a rectangle which is the same as this one moved by a given amount. */ const Rectangle operator+ (const Point& deltaPosition) const throw() { return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); } + /** Moves this rectangle by a given amount. */ Rectangle& operator+= (const Point& deltaPosition) throw() { x += deltaPosition.getX(); y += deltaPosition.getY(); return *this; } + /** Returns a rectangle which is the same as this one moved by a given amount. */ const Rectangle operator- (const Point& deltaPosition) const throw() { return Rectangle (x - deltaPosition.getX(), y - deltaPosition.getY(), w, h); } + /** Moves this rectangle by a given amount. */ Rectangle& operator-= (const Point& deltaPosition) throw() { x -= deltaPosition.getX(); y -= deltaPosition.getY(); return *this; } + /** 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 ValueType deltaX, const ValueType deltaY) throw() { @@ -10525,6 +20176,11 @@ public: setBounds (x - deltaX, y - deltaY, nw, nh); } + /** 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 ValueType deltaX, const ValueType deltaY) const throw() { @@ -10533,52 +20189,69 @@ public: return Rectangle (x - deltaX, y - deltaY, nw, nh); } + /** 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 ValueType deltaX, const ValueType deltaY) throw() { expand (-deltaX, -deltaY); } + /** 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 ValueType deltaX, const ValueType deltaY) const throw() { return expanded (-deltaX, -deltaY); } + /** Returns true if the two rectangles are identical. */ bool operator== (const Rectangle& other) const throw() { return x == other.x && y == other.y && w == other.w && h == other.h; } + /** Returns true if the two rectangles are not identical. */ bool operator!= (const Rectangle& other) const throw() { return x != other.x || y != other.y || w != other.w || h != other.h; } + /** Returns true if this co-ordinate is inside the rectangle. */ bool contains (const ValueType xCoord, const ValueType yCoord) const throw() { return xCoord >= x && yCoord >= y && xCoord < x + w && yCoord < y + h; } + /** Returns true if this co-ordinate is inside the rectangle. */ bool contains (const Point& point) const throw() { return point.getX() >= x && point.getY() >= y && point.getX() < x + w && point.getY() < y + h; } + /** Returns true if this other rectangle is completely inside this one. */ bool 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; } + /** Returns the nearest point to the specified point that lies within this rectangle. */ const Point getConstrainedPoint (const Point& point) const throw() { return Point (jlimit (x, x + w, point.getX()), jlimit (y, y + h, point.getY())); } + /** Returns true if any part of another rectangle overlaps this one. */ bool intersects (const Rectangle& other) const throw() { return x + w > other.x @@ -10588,6 +20261,10 @@ public: && w > ValueType() && h > ValueType(); } + /** 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() { const ValueType nx = jmax (x, other.x); @@ -10601,6 +20278,12 @@ public: return Rectangle(); } + /** 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 (ValueType& otherX, ValueType& otherY, ValueType& otherW, ValueType& otherH) const throw() { const int maxX = jmax (otherX, x); @@ -10621,6 +20304,9 @@ public: return false; } + /** Returns the smallest rectangle that contains both this one and the one + passed-in. + */ const Rectangle getUnion (const Rectangle& other) const throw() { const ValueType newX = jmin (x, other.x); @@ -10631,6 +20317,12 @@ public: jmax (y + h, other.y + other.h) - newY); } + /** 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 (x == other.x && getRight() == other.getRight() @@ -10653,6 +20345,12 @@ public: return false; } + /** 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() { int inside = 0; @@ -10676,6 +20374,11 @@ public: return false; } + /** Returns the smallest rectangle that can contain the shape created by applying + a transform to this rectangle. + + This should only be used on floating point rectangles. + */ const Rectangle transformed (const AffineTransform& transform) const throw() { float x1 = x, y1 = y; @@ -10696,6 +20399,10 @@ public: jmax (y1, y2, y3, y4) - y); } + /** Returns the smallest integer-aligned rectangle that completely contains this one. + This is only relevent for floating-point rectangles, of course. + @see toFloat() + */ const Rectangle getSmallestIntegerContainer() const throw() { const int x1 = (int) floorf ((float) x); @@ -10706,12 +20413,22 @@ public: return Rectangle (x1, y1, x2 - x1, y2 - y1); } + /** Casts this rectangle to a Rectangle. + Obviously this is mainly useful for rectangles that use integer types. + @see getSmallestIntegerContainer + */ const Rectangle toFloat() const throw() { return Rectangle (static_cast (x), static_cast (y), static_cast (w), static_cast (h)); } + /** Static utility to intersect two sets of rectangular co-ordinates. + + Returns false if the two regions didn't overlap. + + @see intersectRectangle + */ static bool intersectRectangles (ValueType& x1, ValueType& y1, ValueType& w1, ValueType& h1, const ValueType x2, const ValueType y2, const ValueType w2, const ValueType h2) throw() { @@ -10733,6 +20450,15 @@ public: return false; } + /** 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 { String s; @@ -10741,6 +20467,16 @@ public: return s; } + /** 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) { StringArray toks; @@ -10767,60 +20503,131 @@ private: #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 (int flags_) throw() : flags (flags_) {} + /** Creates a copy of another Justification object. */ Justification (const Justification& other) throw(); + /** Copies another Justification object. */ 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 (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, int w, int h, int spaceX, int spaceY, int spaceW, 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 }; @@ -10841,24 +20648,47 @@ class Path; class RectangleList; class Image; +/** + A table of horizontal scan-line segments - used for rasterising Paths. + + @see Path, Graphics +*/ class JUCE_API EdgeTable { public: + /** Creates an edge table containing a path. + + A table is created with a fixed vertical range, and only sections of the path + which lie within this range will be added to the table. + + @param clipLimits only the region of the path that lies within this area will be added + @param pathToAdd the path to add to the table + @param transform a transform to apply to the path being added + */ EdgeTable (const Rectangle& clipLimits, const Path& pathToAdd, const AffineTransform& transform); + /** Creates an edge table containing a rectangle. + */ EdgeTable (const Rectangle& rectangleToAdd); + /** Creates an edge table containing a rectangle list. + */ EdgeTable (const RectangleList& rectanglesToAdd); + /** Creates an edge table containing a rectangle. + */ EdgeTable (float x, float y, float w, float h); + /** Creates a copy of another edge table. */ EdgeTable (const EdgeTable& other); + /** Copies from another edge table. */ EdgeTable& operator= (const EdgeTable& other); + /** Destructor. */ ~EdgeTable(); void clipToRectangle (const Rectangle& r) throw(); @@ -10869,8 +20699,26 @@ public: const Rectangle& getMaximumBounds() const throw() { return bounds; } void translate (float dx, int dy) 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) + */ template void iterate (EdgeTableIterationCallback& iterationCallback) const throw() { @@ -10973,48 +20821,188 @@ private: class Image; +/** + 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 { public: + /** Creates an empty path. */ Path(); + /** Creates a copy of another path. */ Path (const Path& other); + /** Destructor. */ ~Path(); + /** Copies this path from another one. */ Path& operator= (const Path& other); + /** 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. + */ const Rectangle getBounds() const throw(); + /** Returns the smallest rectangle that contains all points within the path + after it's been transformed with the given tranasform matrix. + */ const Rectangle getBoundsTransformed (const AffineTransform& transform) 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. + + The tolerence parameter is passed to the PathFlatteningIterator that + is used to trace the path - for more info about it, see the notes for + the PathFlatteningIterator constructor. + + @see closeSubPath, setUsingNonZeroWinding + */ bool contains (float x, float y, float tolerence = 10.0f) const; + /** 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. + + The tolerence parameter is passed to the PathFlatteningIterator that + is used to trace the path - for more info about it, see the notes for + the PathFlatteningIterator constructor. + + @see closeSubPath, setUsingNonZeroWinding + */ bool contains (const Point& point, float tolerence = 10.0f) const; + /** 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. + + The tolerence parameter is passed to the PathFlatteningIterator that + is used to trace the path - for more info about it, see the notes for + the PathFlatteningIterator constructor. + */ bool intersectsLine (const Line& line, float tolerence = 10.0f); + /** Cuts off parts of a line to keep the parts that are either inside or + outside this 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 keepSectionOutsidePath if true, it's the section outside the path + that will be kept; if false its the section inside + the path + */ const Line getClippedLine (const Line& line, bool keepSectionOutsidePath) const; + /** 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 (float startX, float startY); + /** 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(); + /** 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 (float endX, float endY); + /** 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 (float controlPointX, float controlPointY, float endPointX, float endPointY); + /** 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 (float controlPoint1X, float controlPoint1Y, float controlPoint2X, @@ -11022,35 +21010,133 @@ public: float endPointX, float endPointY); + /** 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 (float x, float y, float width, float height); + /** 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 Rectangle& rectangle); + /** 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 (float x, float y, float width, float height, float cornerSize); + /** 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 (float x, float y, float width, float height, float cornerSizeX, float cornerSizeY); + /** 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 (float x1, float y1, float x2, float y2, float x3, float y3); + /** 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 (float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + /** 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 (float x, float y, float width, float height); + /** 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 (float x, float y, float width, float height, float fromRadians, float toRadians, bool startAsNewSubPath = false); + /** 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 (float centreX, float centreY, float radiusX, float radiusY, float rotationOfEllipse, @@ -11058,22 +21144,60 @@ public: float toRadians, bool startAsNewSubPath = false); + /** 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 (float x, float y, float width, float height, float fromRadians, float toRadians, 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 (float startX, float startY, float endX, float endY, float lineThickness); + /** 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 (float startX, float startY, float endX, float endY, float lineThickness, float arrowheadWidth, float arrowheadLength); + /** Adds a star shape to the path. + + */ void addStar (float centreX, float centreY, int numberOfPoints, @@ -11081,6 +21205,20 @@ public: float outerRadius, 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, @@ -11090,28 +21228,114 @@ public: 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); + /** 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); + /** 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 (float x, float y, float width, float height, 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 (float x, float y, float width, float height, bool preserveProportions, const Justification& justificationType = Justification::centred) const; + /** 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 (float cornerRadius) const; + /** 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 (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 { return useNonZeroWinding; } + /** Iterates the lines and curves that a path contains. + + @see Path, PathFlatteningIterator + */ class JUCE_API Iterator { public: @@ -11119,6 +21343,12 @@ 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 @@ -11142,14 +21372,43 @@ public: 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 void* data, int numberOfBytes); + /** 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 @@ -11174,26 +21433,71 @@ private: class Font; +/** A typeface represents a size-independent font. + + This base class is abstract, but calling createSystemTypefaceFor() will return + a platform-specific subclass that can be used. + + The CustomTypeface subclass allow you to build your own typeface, and to + load and save it in the Juce typeface format. + + Normally you should never need to deal directly with Typeface objects - the Font + class does everything you typically need for rendering text. + + @see CustomTypeface, Font +*/ class JUCE_API Typeface : public ReferenceCountedObject { public: + /** A handy typedef for a pointer to a typeface. */ typedef ReferenceCountedObjectPtr Ptr; + /** Returns the name of the typeface. + @see Font::getTypefaceName + */ const String getName() const throw() { return name; } + /** Creates a new system typeface. */ static const Ptr createSystemTypefaceFor (const Font& font); + /** Destructor. */ virtual ~Typeface(); + /** Returns the ascent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies above + its baseline. + */ virtual float getAscent() const = 0; + /** Returns the descent of the font, as a proportion of its height. + The height is considered to always be normalised as 1.0, so this will be a + value less that 1.0, indicating the proportion of the font that lies below + its baseline. + */ virtual float getDescent() const = 0; + /** Measures the width of a line of text. + + The distance returned is based on the font having an normalised height of 1.0. + + You should never need to call this directly! Use Font::getStringWidth() instead! + */ virtual float getStringWidth (const String& text) = 0; + /** Converts a line of text into its glyph numbers and their positions. + + The distances returned are based on the font having an normalised height of 1.0. + + You should never need to call this directly! Use Font::getGlyphPositions() instead! + */ virtual void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets) = 0; + /** Returns the outline for a glyph. + + The path returned will be normalised to a font height of 1.0. + */ virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0; juce_UseDebuggingNewOperator @@ -11208,28 +21512,74 @@ private: Typeface& operator= (const Typeface&); }; +/** A typeface that can be populated with custom glyphs. + + You can create a CustomTypeface if you need one that contains your own glyphs, + or if you need to load a typeface from a Juce-formatted binary stream. + + If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() + to copy glyphs into this face. + + @see Typeface, Font +*/ class JUCE_API CustomTypeface : public Typeface { public: + /** Creates a new, empty typeface. */ CustomTypeface(); + /** Loads a typeface from a previously saved stream. + The stream must have been created by writeToStream(). + @see writeToStream + */ explicit CustomTypeface (InputStream& serialisedTypefaceStream); + /** Destructor. */ ~CustomTypeface(); + /** Resets this typeface, deleting all its glyphs and settings. */ void clear(); + /** Sets the vital statistics for the typeface. + @param name the typeface's name + @param ascent the ascent - this is normalised to a height of 1.0 and this is + the value that will be returned by Typeface::getAscent(). The + descent is assumed to be (1.0 - ascent) + @param isBold should be true if the typeface is bold + @param isItalic should be true if the typeface is italic + @param defaultCharacter the character to be used as a replacement if there's + no glyph available for the character that's being drawn + */ void setCharacteristics (const String& name, float ascent, bool isBold, bool isItalic, juce_wchar defaultCharacter) throw(); + /** Adds a glyph to the typeface. + + The path that is passed in is normalised so that the font height is 1.0, and its + origin is the anchor point of the character on its baseline. + + The width is the nominal width of the character, and any extra kerning values that + are specified will be added to this width. + */ void addGlyph (juce_wchar character, const Path& path, float width) throw(); + /** Specifies an extra kerning amount to be used between a pair of characters. + The amount will be added to the nominal width of the first character when laying out a string. + */ void addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) throw(); + /** Adds a range of glyphs from another typeface. + This will attempt to pull in the paths and kerning information from another typeface and + add it to this one. + */ void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) throw(); + /** Saves this typeface as a Juce-formatted font file. + A CustomTypeface can be created to reload the data that is written - see the CustomTypeface + constructor. + */ bool writeToStream (OutputStream& outputStream); // The following methods implement the basic Typeface behaviour. @@ -11247,6 +21597,12 @@ protected: float ascent; bool isBold, isItalic; + /** If a subclass overrides this, it can load glyphs into the font on-demand. + When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a + particular character and there's no corresponding glyph, they'll call this + method so that a subclass can try to add that glyph, returning true if it + manages to do so. + */ virtual bool loadGlyphIfPossible (juce_wchar characterNeeded); private: @@ -11268,10 +21624,22 @@ private: class LowLevelGraphicsContext; +/** + 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 */ @@ -11280,88 +21648,278 @@ public: 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 + */ Font (float fontHeight, 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 + */ Font (const String& typefaceName, float fontHeight, int styleFlags) throw(); + /** Creates a copy of another Font object. */ Font (const Font& other) throw(); + /** Creates a font for a typeface. */ Font (const Typeface::Ptr& 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. */ 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. + + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + If you need to know the exact typeface name being used, you can call + Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. + + 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. + + This may also be set to Font::getDefaultSansSerifFontName(), Font::getDefaultSerifFontName(), + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font names, + but are generic names that are used to represent the various default fonts. + + If you need to know the exact typeface name being used, you can call + Font::getTypeface()->getTypefaceName(), which will give you the platform-specific name. + */ const String& getTypefaceName() const throw() { return font->typefaceName; } + /** Returns a typeface name that represents the default sans-serif font. + + This is also the typeface that will be used when a font is created without + specifying any typeface details. + + Note that this method just returns a generic placeholder string that means "the default + sans-serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). + + @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName + */ static const String getDefaultSansSerifFontName() throw(); + /** Returns a typeface name that represents the default sans-serif font. + + Note that this method just returns a generic placeholder string that means "the default + serif font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). + + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName + */ static const String getDefaultSerifFontName() throw(); + /** Returns a typeface name that represents the default sans-serif font. + + Note that this method just returns a generic placeholder string that means "the default + monospaced font" - it's not the actual name of this font. To get the actual name, use + getPlatformDefaultFontNames() or LookAndFeel::getTypefaceForFont(). + + @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName + */ static const String getDefaultMonospacedFontName() throw(); + /** Returns the typeface names of the default fonts on the current platform. */ static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed); + /** 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 font->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 font->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 (int newFlags) throw(); + /** Makes the font bold or non-bold. */ void setBold (bool shouldBeBold) throw(); + /** Returns true if the font is bold. */ bool isBold() const throw(); + /** Makes the font italic or non-italic. */ void setItalic (bool shouldBeItalic) throw(); + /** Returns true if the font is italic. */ bool isItalic() const throw(); + /** Makes the font underlined or non-underlined. */ void setUnderline (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 (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 font->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 (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 font->kerning; } + /** Changes all the font's characteristics with one call. */ void setSizeAndStyle (float newHeight, int newStyleFlags, float newHorizontalScale, float newKerningAmount) 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 series of glyph numbers and their x offsets needed to represent a string. + + An extra x offset is added at the end of the run, to indicate where the right hand + edge of the last character is. + */ void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) 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 (Array& 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(); + /** 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 @@ -11397,10 +21955,20 @@ private: #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. @@ -11413,6 +21981,7 @@ public: 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. */ @@ -11421,21 +21990,67 @@ public: 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 (float strokeThickness, JointStyle jointStyle = mitered, EndCapStyle endStyle = butt) throw(); + /** Createes a copy of another stroke type. */ PathStrokeType (const PathStrokeType& other) throw(); + /** Copies another stroke onto this one. */ 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, float extraAccuracy = 1.0f) const; + /** 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, @@ -11443,16 +22058,21 @@ public: const AffineTransform& transform = AffineTransform::identity, float extraAccuracy = 1.0f) const; + /** 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: @@ -11492,12 +22112,23 @@ private: class PixelRGB; class PixelAlpha; +/** + 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_) { @@ -11512,6 +22143,11 @@ public: 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. + */ forcedinline void blend (const PixelARGB& src) throw() { uint32 sargb = src.getARGB(); @@ -11523,10 +22159,25 @@ public: argb = sargb; } + /** 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 PixelAlpha& src) throw(); + /** 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 PixelRGB& src) throw(); + /** 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() { @@ -11543,6 +22194,9 @@ public: 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() { @@ -11559,17 +22213,23 @@ public: 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; @@ -11583,6 +22243,7 @@ public: 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; @@ -11591,6 +22252,7 @@ public: components.a = a; } + /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() throw() { const uint32 alpha = components.a; @@ -11612,6 +22274,7 @@ public: } } + /** Unpremultiplies the pixel's RGB values. */ forcedinline void unpremultiply() throw() { const uint32 alpha = components.a; @@ -11649,6 +22312,7 @@ public: } } + /** The indexes of the different components in the byte layout of this type of colour. */ #if JUCE_BIG_ENDIAN enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; #else @@ -11673,12 +22337,24 @@ private: } 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); @@ -11695,6 +22371,11 @@ public: 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(); @@ -11715,6 +22396,11 @@ public: forcedinline void blend (const PixelAlpha& src) throw(); + /** 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() { @@ -11733,6 +22419,9 @@ public: 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() { @@ -11747,6 +22436,12 @@ public: 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() { @@ -11755,10 +22450,13 @@ public: 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_; @@ -11766,8 +22464,10 @@ public: 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() @@ -11775,6 +22475,7 @@ public: r = g = b = (uint8) (((int) r + (int) g + (int) b) / 3); } + /** The indexes of the different components in the byte layout of this type of colour. */ #if JUCE_MAC enum { indexR = 0, indexG = 1, indexB = 2 }; #else @@ -11796,12 +22497,24 @@ forcedinline void PixelARGB::blend (const PixelRGB& src) throw() set (src); } +/** + Represents an 8-bit single-channel pixel, and can perform compositing operations on it. + + This is used internally by the imaging classes. + + @see PixelARGB, PixelRGB +*/ class JUCE_API PixelAlpha { public: + /** Creates a pixel without defining its colour. */ PixelAlpha() throw() {} ~PixelAlpha() throw() {} + /** Creates a pixel from a 32-bit argb value. + + (The argb format is that used by PixelARGB) + */ PixelAlpha (const uint32 argb) throw() { a = (uint8) (argb >> 24); @@ -11816,6 +22529,11 @@ public: forcedinline uint8 getGreen() const throw() { return 0; } forcedinline uint8 getBlue() const throw() { return 0; } + /** 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() { @@ -11823,6 +22541,11 @@ public: a = (uint8) ((a * (0x100 - srcA) >> 8) + srcA); } + /** 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() { @@ -11831,23 +22554,32 @@ public: a = (uint8) ((a * (0x100 - srcAlpha) >> 8) + srcAlpha); } + /** 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() { a += ((src,getAlpha() - a) * amount) >> 8; } + /** 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() { a = src.getAlpha(); } + /** Replaces the colour's alpha value with another one. */ forcedinline void setAlpha (const uint8 newAlpha) throw() { a = newAlpha; } + /** Multiplies the colour's alpha value with another one. */ forcedinline void multiplyAlpha (int multiplier) throw() { ++multiplier; @@ -11859,15 +22591,18 @@ public: a = (uint8) (a * multiplier); } + /** Sets the pixel's colour from individual components. */ forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) throw() { a = a_; } + /** Premultiplies the pixel's RGB values by its alpha. */ forcedinline void premultiply() throw() { } + /** Unpremultiplies the pixel's RGB values. */ forcedinline void unpremultiply() throw() { } @@ -11876,6 +22611,7 @@ public: { } + /** The indexes of the different components in the byte layout of this type of colour. */ enum { indexA = 0 }; private: @@ -11908,137 +22644,317 @@ forcedinline void PixelARGB::blend (const PixelAlpha& src) throw() #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 << 8) | 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 (uint32 argb) throw(); + /** Creates an opaque colour using 8-bit red, green and blue values */ Colour (uint8 red, uint8 green, uint8 blue) throw(); + /** Creates an opaque colour using 8-bit red, green and blue values */ static const Colour fromRGB (uint8 red, uint8 green, uint8 blue) throw(); + /** Creates a colour using 8-bit red, green, blue and alpha values. */ Colour (uint8 red, uint8 green, uint8 blue, uint8 alpha) throw(); + /** Creates a colour using 8-bit red, green, blue and alpha values. */ static const Colour fromRGBA (uint8 red, uint8 green, uint8 blue, 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 (uint8 red, uint8 green, uint8 blue, float alpha) throw(); + /** Creates a colour using 8-bit red, green, blue and float alpha values. */ static const Colour fromRGBAFloat (uint8 red, uint8 green, uint8 blue, 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 (float hue, float saturation, float brightness, 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 (float hue, float saturation, float brightness, 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. + */ static const Colour fromHSV (float hue, float saturation, float brightness, float alpha) throw(); + /** Destructor. */ ~Colour() throw(); + /** Copies another Colour object. */ 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 (uint8 newAlpha) const throw(); + /** Returns a colour that's the same colour as this one, but with a new alpha value. */ const Colour withAlpha (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 (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 a colour that lies somewhere between this one and another. + + If amountOfOther is zero, the result is 100% this colour, if amountOfOther + is 1.0, the result is 100% of the other colour. + */ const Colour interpolatedWith (const Colour& other, float proportionOfOther) 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 (float newHue) const throw(); + /** Returns a copy of this colour with a different saturation. */ const Colour withSaturation (float newSaturation) const throw(); + /** Returns a copy of this colour with a different brightness. + @see brighter, darker, withMultipliedBrightness + */ const Colour withBrightness (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 (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 (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 (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 (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 (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; + /** Reads the colour from a string that was created with toString(). + */ static const Colour fromString (const String& encodedColourString); + /** Returns the colour as a hex string in the form RRGGBB or AARRGGBB. */ const String toDisplayString (bool includeAlphaValue) const; juce_UseDebuggingNewOperator @@ -12050,6 +22966,11 @@ private: #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: @@ -12100,6 +23021,13 @@ public: 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); @@ -12125,37 +23053,99 @@ private: #ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ #define __JUCE_COLOURGRADIENT_JUCEHEADER__ +/** + Describes the layout and colours that should be used to paint a colour gradient. + + @see Graphics::setGradientFill +*/ 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, float x1, float y1, const Colour& colour2, float x2, float y2, bool isRadial); + /** 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(); + /** 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(); + /** 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 (double proportionAlongGradient, const Colour& colour); + /** Multiplies the alpha value of all the colours by the given scale factor */ void multiplyOpacity (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 (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 (int index) const throw(); + /** Returns the an interpolated colour at any position along the gradient. + @param position the position along the gradient, between 0 and 1 + */ const Colour getColourAtPosition (float position) const throw(); + /** Creates a set of interpolated premultiplied ARGB values. + This will resize the HeapBlock, fill it with the colours, and will return the number of + colours that it added. + */ int createLookupTable (const AffineTransform& transform, HeapBlock & resultLookupTable) const; + /** 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; @@ -12164,6 +23154,11 @@ public: 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; juce_UseDebuggingNewOperator @@ -12177,45 +23172,101 @@ private: class Image; +/** + Represents a colour or fill pattern to use for rendering paths. + + This is used by the Graphics and DrawablePath classes as a way to encapsulate + a brush type. It can either be a solid colour, a gradient, or a tiled image. + + @see Graphics::setFillType, DrawablePath::setFill +*/ class JUCE_API FillType { public: + /** Creates a default fill type, of solid black. */ FillType() throw(); + /** Creates a fill type of a solid colour. + @see setColour + */ FillType (const Colour& colour) throw(); + /** Creates a gradient fill type. + @see setGradient + */ FillType (const ColourGradient& gradient); + /** Creates a tiled image fill type. The transform allows you to set the scaling, offset + and rotation of the pattern. + @see setTiledImage + */ FillType (const Image& image, const AffineTransform& transform) throw(); + /** Creates a copy of another FillType. */ FillType (const FillType& other); + /** Makes a copy of another FillType. */ FillType& operator= (const FillType& other); + /** Destructor. */ ~FillType() throw(); + /** Returns true if this is a solid colour fill, and not a gradient or image. */ bool isColour() const throw() { return gradient == 0 && image == 0; } + /** Returns true if this is a gradient fill. */ bool isGradient() const throw() { return gradient != 0; } + /** Returns true if this is a tiled image pattern fill. */ bool isTiledImage() const throw() { return image != 0; } + /** Turns this object into a solid colour fill. + If the object was an image or gradient, those fields will no longer be valid. */ void setColour (const Colour& newColour) throw(); + /** Turns this object into a gradient fill. */ void setGradient (const ColourGradient& newGradient); + /** Turns this object into a tiled image fill type. The transform allows you to set + the scaling, offset and rotation of the pattern. + */ void setTiledImage (const Image& image, const AffineTransform& transform) throw(); + /** Changes the opacity that should be used. + If the fill is a solid colour, this just changes the opacity of that colour. For + gradients and image tiles, it changes the opacity that will be used for them. + */ void setOpacity (float newOpacity) throw(); + /** Returns the current opacity to be applied to the colour, gradient, or image. + @see setOpacity + */ float getOpacity() const throw() { return colour.getFloatAlpha(); } + /** The solid colour being used. + + If the fill type is not a solid colour, the alpha channel of this colour indicates + the opacity that should be used for the fill, and the RGB channels are ignored. + */ Colour colour; + /** Returns the gradient that should be used for filling. + This will be zero if the object is some other type of fill. + If a gradient is active, the overall opacity with which it should be applied + is indicated by the alpha channel of the colour variable. + */ ScopedPointer gradient; + /** Returns the image that should be used for tiling. + The FillType object just keeps a pointer to this image, it doesn't own it, so you have to + be careful to make sure the image doesn't get deleted while it's being used. + If an image fill is active, the overall opacity with which it should be applied + is indicated by the alpha channel of the colour variable. + */ const Image* image; + /** The transform that should be applied to the image or gradient that's being drawn. + */ AffineTransform transform; juce_UseDebuggingNewOperator @@ -12229,48 +23280,99 @@ public: #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 (int flags_) throw() : flags (flags_) {} + /** Creates a copy of another RectanglePlacement object. */ RectanglePlacement (const RectanglePlacement& other) throw(); + /** Copies another RectanglePlacement object. */ 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, without changing its + aspect ratio. 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 (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, @@ -12280,6 +23382,9 @@ public: double destinationW, 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, @@ -12301,86 +23406,277 @@ 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. + */ explicit Graphics (Image& imageToDrawOnto); + /** Destructor. */ ~Graphics(); + /** 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 + */ void setColour (const Colour& newColour); + /** Changes the opacity to use with the current colour. + + If a solid colour is being used for drawing, this changes its opacity + to this new value (i.e. it doesn't multiply the colour's opacity by this amount). + + If a gradient is being used, this will have no effect on it. + + A value of 0.0 is completely transparent, 1.0 is completely opaque. + */ void setOpacity (const float newOpacity); + /** Sets the context to use a gradient for its fill pattern. + */ void setGradientFill (const ColourGradient& gradient); + /** Sets the context to use a tiled image pattern for filling. + Make sure that you don't delete this image while it's still being used by + this context! + */ void setTiledImageFill (const Image& imageToUse, int anchorX, int anchorY, float opacity); + /** Changes the current fill settings. + @see setColour, setGradientFill, setTiledImageFill + */ void setFillType (const FillType& newFill); + /** 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); + /** 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 (float newFontHeight, int fontStyleFlags = Font::plain); + /** 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, int startX, int baselineY) const; + /** 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, int startX, int baselineY, int maximumLineWidth) const; + /** 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; + /** 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, int x, int y, int width, int height, const Justification& justificationType, bool useEllipsesIfTooBig) const; + /** 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, int x, int y, int width, int height, const Justification& justificationFlags, int maximumNumberOfLines, float minimumHorizontalScale = 0.7f) const; + /** 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; + /** 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; + /** Fills a rectangle with the current colour or brush. + + @see drawRect, fillRoundedRectangle + */ void fillRect (int x, int y, int width, int height) const; + /** Fills a rectangle with the current colour or brush. */ void fillRect (const Rectangle& rectangle) const; + /** 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 (float x, float y, float width, float height) const; + /** Uses the current colour or brush to fill a rectangle with rounded corners. + + @see drawRoundedRectangle, Path::addRoundedRectangle + */ void fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const; + /** Uses the current colour or brush to fill a rectangle with rounded corners. + + @see drawRoundedRectangle, Path::addRoundedRectangle + */ void fillRoundedRectangle (const Rectangle& rectangle, float cornerSize) const; + /** Fills a rectangle with a checkerboard pattern, alternating between two colours. + */ void fillCheckerBoard (int x, int y, int width, int height, int checkWidth, int checkHeight, const Colour& colour1, const Colour& colour2) const; + /** 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 (int x, int y, int width, int height, int lineThickness = 1) const; + /** 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 (float x, float y, float width, float height, float lineThickness = 1.0f) const; + /** 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, int lineThickness = 1) const; + /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners. + + @see fillRoundedRectangle, Path::addRoundedRectangle + */ void drawRoundedRectangle (float x, float y, float width, float height, float cornerSize, float lineThickness) const; + /** 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, float cornerSize, float lineThickness) const; + /** 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. + + If useGradient is true, then the bevel fades out to make it look more curved + and less angular. If sharpEdgeOnOutside is true, the outside of the bevel is + sharp, and it fades towards the centre; if sharpEdgeOnOutside is false, then + the centre edges are sharp and it fades towards the outside. + */ void drawBevel (int x, int y, int width, int height, int bevelThickness, const Colour& topLeftColour = Colours::white, @@ -12388,44 +23684,113 @@ public: bool useGradient = true, bool sharpEdgeOnOutside = true) const; + /** Draws a pixel using the current colour or brush. + */ void setPixel (int x, int y) const; + /** 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 (float x, float y, float width, float height) const; + /** Draws an elliptical stroke using the current colour or brush. + + @see fillEllipse, Path::addEllipse + */ void drawEllipse (float x, float y, float width, float height, float lineThickness) const; + /** 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; + /** Draws a line between two points with a given thickness. + + @see Path::addLineSegment + */ void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; + /** 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; + /** Draws a line between two points with a given thickness. + + @see Path::addLineSegment + */ void drawLine (const Line& line, float lineThickness) const; + /** 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 (float startX, float startY, float endX, float endY, const float* dashLengths, int numDashLengths, float lineThickness = 1.0f) const; + /** 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 (int x, float top, float bottom) const; + /** 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 (int y, float left, float right) const; + /** Fills a path using the currently selected colour or brush. + */ void fillPath (const Path& path, const AffineTransform& transform = AffineTransform::identity) const; + /** 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; + /** 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 (float startX, float startY, float endX, float endY, float lineThickness, float arrowheadWidth, float arrowheadLength) const; + /** 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. */ @@ -12433,57 +23798,205 @@ public: 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); + /** 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, int topLeftX, int topLeftY, bool fillAlphaChannelWithCurrentBrush = false) const; + /** 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, bool fillAlphaChannelWithCurrentBrush = false) const; + /** 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 subregion is specified within the source image, and all transformations + will be treated as relative to the origin of this sub-region. So, for example if + your subregion is (50, 50, 100, 100), and your transform is a translation of (20, 20), + the resulting pixel drawn at (20, 20) in the destination context is from (50, 50) in + your image. If you want to use the whole image, then Image::getBounds() returns a + suitable rectangle to use as the imageSubRegion parameter. + + 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* imageToDraw, const Rectangle& imageSubRegion, const AffineTransform& transform, bool fillAlphaChannelWithCurrentBrush = false) const; + /** 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* imageToDraw, int destX, int destY, int destWidth, int destHeight, const RectanglePlacement& placementWithinTarget, bool fillAlphaChannelWithCurrentBrush = false) const; + /** Returns the position of the bounding box for the current clipping region. + + @see getClipRegion, clipRegionIntersects + */ const Rectangle getClipBounds() const; + /** 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 (int x, int y, int width, int height) const; + /** Intersects the current clipping region with another region. + + @returns true if the resulting clipping region is non-zero in size + @see setOrigin, clipRegionIntersects + */ bool reduceClipRegion (int x, int y, int width, int height); + /** 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 + */ bool reduceClipRegion (const RectangleList& clipRegion); + /** Intersects the current clipping region with a path. + + @returns true if the resulting clipping region is non-zero in size + @see reduceClipRegion + */ bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform::identity); + /** Intersects the current clipping region with an image's alpha-channel. + + The current clipping path is intersected with the area covered by this image's + alpha-channel, after the image has been transformed by the specified matrix. + + @param image the image whose alpha-channel should be used. If the image doesn't + have an alpha-channel, it is treated as entirely opaque. + @param sourceClipRegion a subsection of the image that should be used. To use the + entire image, just pass a rectangle of bounds + (0, 0, image.getWidth(), image.getHeight()). + @param transform a matrix to apply to the image + @returns true if the resulting clipping region is non-zero in size + @see reduceClipRegion + */ bool reduceClipRegion (const Image& image, const Rectangle& sourceClipRegion, const AffineTransform& transform); + /** Excludes a rectangle to stop it being drawn into. */ void excludeClipRegion (const Rectangle& rectangleToExclude); + /** Returns true if no drawing can be done because the clip region is zero. */ bool isClipEmpty() const; + /** Saves the current graphics state on an internal stack. + + To restore the state, use restoreState(). + */ void saveState(); + /** Restores a graphics state that was previously saved with saveState(). + */ void restoreState(); + /** 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 (int newOriginX, int newOriginY); + /** Resets the current colour, brush, and font to default settings. */ void resetToDefaultState(); + /** Returns true if this context is drawing to a vector-based device, such as a printer. */ bool isVectorDevice() const; 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: @@ -12501,13 +24014,34 @@ private: #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() {} }; @@ -12520,64 +24054,179 @@ public: #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); + /** Creates a list containing just one rectangle. */ RectangleList (const Rectangle& rect); + /** Copies this list from another one. */ RectangleList& operator= (const RectangleList& other); + /** Destructor. */ ~RectangleList(); + /** 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 (int index) const throw(); + /** Removes all rectangles to leave an empty region. */ void clear(); + /** 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 (int x, int y, int width, int height); + /** 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); + /** 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); + /** 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); + /** 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); + /** 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); + /** 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); + /** 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); + /** 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; + /** 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 (int x, 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; + /** 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(); + /** Adds an x and y value to all the co-ordinates. */ void offsetAll (int dx, int dy) throw(); + /** Creates a Path object to represent this region. */ const Path toPath() const; + /** An iterator for accessing all the rectangles in a RectangleList. */ class Iterator { public: @@ -12585,8 +24234,13 @@ public: Iterator (const RectangleList& list) throw(); ~Iterator(); + /** 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 @@ -12615,49 +24269,79 @@ private: #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 (int topGap, int leftGap, int bottomGap, int rightGap) throw(); + /** Creates a border with the given gap on all sides. */ explicit BorderSize (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 (int newTopGap) throw(); + /** Changes the left gap. */ void setLeft (int newLeftGap) throw(); + /** Changes the bottom gap. */ void setBottom (int newBottomGap) throw(); + /** Changes the right gap. */ void setRight (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(); @@ -12677,150 +24361,563 @@ class MouseInputSource; class MouseInputSourceInternal; class ComponentPeer; +/** + 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(); + /** 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 + */ explicit Component (const String& componentName); + /** 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; + /** 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; + /** 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 (int lengthOfFadeOutInMilliseconds, int deltaXToMove = 0, int deltaYToMove = 0, 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; + /** 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 (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* other); + /** Sets whether the component should always be kept at the front of its siblings. + + @see isAlwaysOnTop + */ void setAlwaysOnTop (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 component's top-left position as a Point. */ const Point getPosition() const throw() { return bounds_.getPosition(); } + /** 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 component's bounds, relative to its own origin. + This is like getBounds(), but returns the rectangle in local co-ordinates, In practice, it'll + return a rectangle with position (0, 0), and the same size as this component. + */ const Rectangle getLocalBounds() const throw(); + /** 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, bool includeSiblings) const; + /** Returns this component's x co-ordinate relative the the screen's top-left origin. + @see getX, relativePositionToGlobal + */ int getScreenX() const; + /** Returns this component's y co-ordinate relative the the screen's top-left origin. + @see getY, relativePositionToGlobal + */ int getScreenY() const; + /** Returns the position of this component's top-left corner relative to the screen's top-left. + @see getScreenBounds + */ const Point getScreenPosition() const; + /** Returns the bounds of this component, relative to the screen's top-left. + @see getScreenPosition + */ const Rectangle getScreenBounds() const; + /** Converts a position relative to this component's top-left into a screen co-ordinate. + + @see globalPositionToRelative, relativePositionToOtherComponent + */ const Point relativePositionToGlobal (const Point& relativePosition) const; + /** Converts a screen co-ordinate into a position relative to this component's top-left. + + @see relativePositionToGlobal, relativePositionToOtherComponent + */ const Point globalPositionToRelative (const Point& screenPosition) const; + /** Converts a position relative to this component's top-left into a position + relative to another component's top-left. + + @see relativePositionToGlobal, globalPositionToRelative + */ const Point relativePositionToOtherComponent (const Component* targetComponent, const Point& positionRelativeToThis) const; + /** 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 (int x, 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 (int x, int y); + /** Changes the size of the component. + + A synchronous call to resized() will be occur if the size actually changes. + */ void setSize (int newWidth, 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 (float proportionalX, float proportionalY, float proportionalWidth, 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, 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 (int x, 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 (float x, 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 (int width, int height); + /** Returns a proportion of the component's width. + + This is a handy equivalent of (getWidth() * proportion). + */ int proportionOfWidth (float proportion) const throw(); + /** Returns a proportion of the component's height. + + This is a handy equivalent of (getHeight() * proportion). + */ int proportionOfHeight (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; + /** 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 (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* 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* 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* 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* 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 (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 { @@ -12838,142 +24935,872 @@ public: 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 (bool allowClicksOnThisComponent, 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, 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 (int x, int y); + /** Returns the component at a certain point within this one. + + @param position the co-ordinates to test, relative to this component's top-left. + @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 Point& position); + /** 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(); + /** 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 (int x, int y, int width, int height); + /** 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 (bool shouldBeBuffered); + /** 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, 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* 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* 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 (bool shouldBeOpaque); + /** 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 (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 (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 (bool trueIfChildIsFocused) const; + /** 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 (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; + /** 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 (int newFocusOrderIndex); + /** 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 (bool shouldBeFocusContainer) 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 (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); + /** 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; + /** 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 (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 (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* newListener, bool wantsEventsForAllNestedChildComponents); + /** Deregisters a mouse listener. + @see addMouseListener, MouseListener + */ void removeMouseListener (MouseListener* listenerToRemove); + /** 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* newListener); + /** Removes a previously-registered key listener. + + @see addKeyListener + */ void removeKeyListener (KeyListener* listenerToRemove); + /** 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. + + @param isKeyDown true if a key has been pressed; false if it has been released + + @see keyPressed, KeyPress, getCurrentlyFocusedComponent, addKeyListener + */ virtual bool keyStateChanged (bool isKeyDown); + /** 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. */ @@ -12981,92 +25808,389 @@ public: 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. + */ const Point getMouseXYRelative() const; + /** 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* newListener); + /** Removes a component listener. + + @see addComponentListener + */ void removeComponentListener (ComponentListener* listenerToRemove); + /** 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 (int commandId); + /** 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 (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 (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 number of components that are currently in a modal state. + @see getCurrentlyModalComponent + */ static int JUCE_CALLTYPE getNumCurrentlyModalComponents() throw(); + /** Returns one of the components that are currently modal. + + The index specifies which of the possible modal components to return. The order + of the components in this list is the reverse of the order in which they became + modal - so the component at index 0 is always the active component, and the others + are progressively earlier ones that are themselves now blocked by later ones. + + @returns the modal component, or null if no components are modal (or if the + index is out of range) + @see getNumCurrentlyModalComponents, runModalLoop, isCurrentlyModal + */ static Component* JUCE_CALLTYPE getCurrentlyModalComponent (int index = 0) 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; + /** 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 the set of properties that belong to this component. + Each component has a NamedValueSet object which you can use to attach arbitrary + items of data to it. + */ NamedValueSet& getProperties() throw() { return properties; } + /** Returns the set of properties that belong to this component. + Each component has a NamedValueSet object which you can use to attach arbitrary + items of data to it. + */ const NamedValueSet& getProperties() const throw() { return properties; } + /** 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 (int colourId, bool inheritFromParent = false) const; + /** 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 (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 (int colourId); + /** Returns true if the specified colour ID has been explicitly set for this + component using the setColour() method. + */ bool isColourSpecified (int colourId) const; + /** 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; + /** 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; + /** 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; } + /** Holds a pointer to some type of Component, which automatically becomes null if + the component is deleted. + + If you're using a component which may be deleted by another event that's outside + of your control, use a SafePointer instead of a normal pointer to refer to it, + and you can test whether it's null before using it to see if something has deleted + it. + + The ComponentType typedef must be Component, or some subclass of Component. + + Note that this class isn't thread-safe, and assumes that all the code that uses + it is running on the message thread. + */ template class SafePointer : private ComponentListener { public: + /** Creates a null SafePointer. */ SafePointer() : comp (0) {} + /** Creates a SafePointer that points at the given component. */ SafePointer (ComponentType* const component) : comp (component) { attach(); } + /** Creates a copy of another SafePointer. */ SafePointer (const SafePointer& other) : comp (other.comp) { attach(); } + /** Destructor. */ ~SafePointer() { detach(); } + /** Copies another pointer to this one. */ SafePointer& operator= (const SafePointer& other) { return operator= (other.comp); } + /** Copies another pointer to this one. */ SafePointer& operator= (ComponentType* const newComponent) { detach(); @@ -13075,8 +26199,10 @@ public: return *this; } + /** Returns the component that this pointer refers to, or null if the component no longer exists. */ operator ComponentType*() const throw() { return comp; } + /** Returns the component that this pointer refers to, or null if the component no longer exists. */ ComponentType* getComponent() const throw() { return comp; } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ @@ -13095,12 +26221,24 @@ public: void componentBeingDeleted (Component&) { comp = 0; } }; + /** A class to keep an eye on one or two components and check for them being deleted. + + This is designed for use with the ListenerList::callChecked() methods, to allow + the list iterator to stop cleanly if the component is deleted by a listener callback + while the list is still being iterated. + */ class BailOutChecker { public: + /** Creates a checker that watches either one or two components. + component1 must be a valid component; component2 can be null if you only need + to check on one component. + */ BailOutChecker (Component* component1, Component* component2 = 0); + /** Returns true if either of the two components have been deleted since this + object was created. */ bool shouldBailOut() const throw(); private: @@ -13222,10 +26360,17 @@ private: virtual void keyStateChanged() {}; 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&); }; @@ -13242,93 +26387,258 @@ protected: #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 { explicit ApplicationCommandInfo (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, 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 (bool isActive) throw(); + /** An easy way to set or remove the isTicked bit in the structure's flags field. + */ void setTicked (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 ('s', ModifierKeys::commandModifier); + @endcode + instead of + @code + myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier)); + @endcode + */ void addDefaultKeypress (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. */ @@ -13337,35 +26647,148 @@ public: 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 (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 @@ -13404,70 +26827,275 @@ private: #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 "Super JUCE-o-matic"; + } + + const String getApplicationVersion() + { + return "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, 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. + + @see MessageManager, DeletedAtShutdown + */ static void quit(); + /** 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 (int newReturnValue) throw(); + /** Returns the value that has been set as the application's exit code. + @see setApplicationReturnValue + */ int getApplicationReturnValue() const throw() { return appReturnValue; } + /** Returns the application's command line params. + */ const String getCommandLineParameters() const throw() { return commandLineParameters; } // These are used by the START_JUCE_APPLICATION() macro and aren't for public use. + /** @internal */ static int main (String& commandLine, JUCEApplication* newApp); + /** @internal */ static int main (int argc, const char* argv[], JUCEApplication* newApp); + /** @internal */ static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber); + /** @internal */ ApplicationCommandTarget* getNextCommandTarget(); + /** @internal */ void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); + /** @internal */ void getAllCommands (Array & commands); + /** @internal */ bool perform (const InvocationInfo& info); + /** @internal */ void actionListenerCallback (const String& message); private: @@ -13481,7 +27109,9 @@ private: JUCEApplication& operator= (const JUCEApplication&); public: + /** @internal */ bool initialiseApp (String& commandLine); + /** @internal */ static int shutdownAppAndClearUp(); }; @@ -13512,14 +27142,35 @@ public: #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(); + /** 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: @@ -13537,26 +27188,87 @@ private: 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 (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: @@ -13575,73 +27287,225 @@ class MouseInputSource; class MouseInputSourceInternal; class MouseListener; +/** + 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(); + /** 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 (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 (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 (const Point& position, bool clippedToWorkArea = true) const; + /** Returns the mouse position. + + The co-ordinates are relative to the top-left of the main monitor. + */ static const Point getMousePosition(); + /** 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 (const Point& newPosition); + /** Returns the last position at which a mouse button was pressed. + */ static const Point getLastMouseDownPosition() 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 (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* listener); + /** Unregisters a MouseListener that was added with the addGlobalMouseListener() + method. + + @see addGlobalMouseListener + */ void removeGlobalMouseListener (MouseListener* listener); + /** Registers a MouseListener that will receive a callback whenever the focused + component changes. + */ void addFocusChangeListener (FocusChangeListener* listener); + /** Unregisters a listener that was added with addFocusChangeListener(). */ void removeFocusChangeListener (FocusChangeListener* listener); + /** Takes a component and makes it full-screen, removing the taskbar, dock, etc. + + The component must already be on the desktop for this method to work. It will + be resized to completely fill the screen and any extraneous taskbars, menu bars, + etc will be hidden. + + To exit kiosk mode, just call setKioskModeComponent (0). When this is called, + the component that's currently being used will be resized back to the size + and position it was in before being put into this mode. + + If allowMenusAndBars is true, things like the menu and dock (on mac) are still + allowed to pop up when the mouse moves onto them. If this is false, it'll try + to hide as much on-screen paraphenalia as possible. + */ void setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars = true); + /** Returns the component that is currently being used in kiosk-mode. + + This is the component that was last set by setKioskModeComponent(). If none + has been set, this returns 0. + */ Component* getKioskModeComponent() const throw() { return kioskModeComponent; } + /** 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 (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 Point& screenPosition) const; + /** Returns the number of MouseInputSource objects the system has at its disposal. + In a traditional single-mouse system, there might be only one object. On a multi-touch + system, there could be one input source per potential finger. + To find out how many mouse events are currently happening, use getNumDraggingMouseSources(). + @see getMouseSource + */ int getNumMouseSources() const throw() { return mouseSources.size(); } + /** Returns one of the system's MouseInputSource objects. + The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return + a null pointer. + In a traditional single-mouse system, there might be only one object. On a multi-touch + system, there could be one input source per potential finger. + */ MouseInputSource* getMouseSource (int index) const throw() { return mouseSources [index]; } + /** Returns the main mouse input device that the system is using. + @see getNumMouseSources() + */ MouseInputSource& getMainMouseSource() const throw() { return *mouseSources.getUnchecked(0); } + /** Returns the number of mouse-sources that are currently being dragged. + In a traditional single-mouse system, this will be 0 or 1, depending on whether a + juce component has the button down on it. In a multi-touch system, this could + be any number from 0 to the number of simultaneous touches that can be detected. + */ int getNumDraggingMouseSources() const throw(); + /** Returns one of the mouse sources that's currently being dragged. + The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is + out of range, or if no mice or fingers are down, this will return a null pointer. + */ MouseInputSource* getDraggingMouseSource (int index) const throw(); juce_UseDebuggingNewOperator + /** Tells this object to refresh its idea of what the screen resolution is. + + (Called internally by the native code). + */ void refreshMonitorSizes(); + /** True if the OS supports semitransparent windows */ static bool canUseSemiTransparentWindows() throw(); private: @@ -13699,59 +27563,272 @@ private: 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 (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 (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 (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 (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 (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 (CommandID commandID, 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, 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 (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* 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 (CommandID commandID, ApplicationCommandInfo& upToDateInfo); + /** Registers a listener that will be called when various events occur. */ void addListener (ApplicationCommandManagerListener* listener) throw(); + /** Deregisters a previously-added listener. */ void removeListener (ApplicationCommandManagerListener* 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 @@ -13775,14 +27852,30 @@ private: ApplicationCommandManager& operator= (const ApplicationCommandManager&); }; +/** + 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; }; @@ -13805,6 +27898,19 @@ public: #ifndef __JUCE_PROPERTIESFILE_JUCEHEADER__ #define __JUCE_PROPERTIESFILE_JUCEHEADER__ +/** 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 @@ -13819,25 +27925,89 @@ public: 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 optionFlags a combination of the flags in the FileFormatOptions + enum, which specify the type of file to save, and other + options. + @param processLock an optional InterprocessLock object that will be used to + prevent multiple threads or processes from writing to the file + at the same time. The PropertiesFile will keep a pointer to + this object but will not take ownership of it - the caller is + responsible for making sure that the lock doesn't get deleted + before the PropertiesFile has been deleted. + */ PropertiesFile (const File& file, int millisecondsBeforeSaving, int optionFlags, InterProcessLock* processLock = 0); + /** Destructor. + + When deleted, the file will first call saveIfNeeded() to flush any changes to disk. + */ ~PropertiesFile(); + /** Returns true if this file was created from a valid (or non-existent) file. + If the file failed to load correctly because it was corrupt or had insufficient + access, this will be false. + */ bool isValidFile() const throw() { return loadedOk; } + /** 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. + The file is flagged as needing to be saved when you change a value, but you can + explicitly set this flag with setNeedsToBeSaved(). + */ bool needsToBeSaved() const; + /** Explicitly sets the flag to indicate whether the file needs saving or not. + @see needsToBeSaved + */ void setNeedsToBeSaved (bool needsToBeSaved); + /** Returns the file that's being used. */ const File getFile() const { return file; } + /** 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, @@ -13846,6 +28016,26 @@ public: int propertiesFileOptions, InterProcessLock* processLock = 0); + /** 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, @@ -13876,32 +28066,110 @@ private: #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, int millisecondsBeforeSaving, 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 (bool testUserSettings, bool testCommonSettings, 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 (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 @@ -13943,24 +28211,101 @@ private: 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* 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 numDestChannels parameter indicates how many pointers this array + contains, but some of these pointers can be null if you don't want to + read data for some of the channels + @param numDestChannels the number of array elements in the destChannels array + @param startSampleInSource the position in the audio file or stream at 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 are out-of-range will be returned + as zeros. + @param numSamplesToRead the number of samples to read. If this is greater than the number + of samples that the file or stream contains. the result will be padded + with zeros + @param fillLeftoverChannelsWithCopies if true, this indicates that if there's no source data available + for some of the channels that you pass in, then they should be filled with + copies of valid source channels. + E.g. if you're reading a mono file and you pass 2 channels to this method, then + if fillLeftoverChannelsWithCopies is true, both destination channels will be filled + with the same data from the file's single channel. If fillLeftoverChannelsWithCopies + was false, then only the first channel would be filled with the file's contents, and + the second would be cleared. If there are many channels, e.g. you try to read 4 channels + from a stereo file, then the last 3 would all end up with copies of the same data. + @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 + */ bool read (int** destSamples, int numDestChannels, int64 startSampleInSource, int numSamplesToRead, bool fillLeftoverChannelsWithCopies); + /** 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, @@ -13968,26 +28313,75 @@ public: 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, double magnitudeRangeMinimum, double magnitudeRangeMaximum, 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; + /** Subclasses must implement this method to perform the low-level read operation. + + Callers should use read() instead of calling this directly. + + @param destSamples the array of destination buffers to fill. Some of these + pointers may be null + @param numDestChannels the number of items in the destSamples array. This + value is guaranteed not to be greater than the number of + channels that this reader object contains + @param startOffsetInDestBuffer the number of samples from the start of the + dest data at which to begin writing + @param startSampleInFile the number of samples into the source data at which + to begin reading. This value is guaranteed to be >= 0. + @param numSamples the number of samples to read + */ virtual bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, @@ -14024,33 +28418,92 @@ private: class AudioFormatReader; class AudioFormatWriter; +/** + 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 (int numChannels, 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, int numChannels, 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. + */ 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 one of the buffer's channels. + + For speed, this doesn't check whether the channel number is out of range, + so be careful when using it! + */ float* getSampleData (const int channelNumber) const throw() { jassert (((unsigned int) channelNumber) < (unsigned int) numChannels); return channels [channelNumber]; } + /** 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 out-of-range, so be careful when using it! + */ float* getSampleData (const int channelNumber, const int sampleOffset) const throw() { @@ -14059,42 +28512,125 @@ public: return channels [channelNumber] + sampleOffset; } + /** Returns an array of pointers to the channels in the buffer. + + Don't modify any of the pointers that are returned, and bear in mind that + these will become invalid if the buffer is resized. + */ float** getArrayOfChannels() const throw() { return channels; } + /** 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 (int newNumChannels, int newNumSamples, bool keepExistingContent = false, bool clearExtraSpace = false, 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, int numChannels, 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 (int startSample, 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 (int channel, int startSample, 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 (int channel, int startSample, int numSamples, 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 (int startSample, int numSamples, 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 (int channel, 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 (int destChannel, int destStartSample, const AudioSampleBuffer& source, @@ -14103,12 +28639,34 @@ public: int numSamples, 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 (int destChannel, int destStartSample, const float* source, int numSamples, 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 (int destChannel, int destStartSample, const float* source, @@ -14116,6 +28674,17 @@ public: 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 (int destChannel, int destStartSample, const AudioSampleBuffer& source, @@ -14123,17 +28692,49 @@ public: 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 (int destChannel, int destStartSample, const float* source, int numSamples) throw(); + /** Copies samples from an array of floats into one of the channels, applying a gain to it. + + @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 + @param gain the gain to apply + + @see addFrom + */ void copyFrom (int destChannel, int destStartSample, const float* source, int numSamples, float gain) throw(); + /** Copies samples from an array of floats into one of the channels, applying a gain ramp. + + @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 + @param startGain the gain to apply to the first sample (this is multiplied with + the source samples before they are copied to this buffer) + @param endGain the gain to apply to the final sample. The gain is linearly + interpolated between the first and last samples. + + @see addFrom + */ void copyFromWithRamp (int destChannel, int destStartSample, const float* source, @@ -14141,23 +28742,46 @@ public: float startGain, float endGain) 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 (int channel, int startSample, int numSamples, float& minVal, float& maxVal) const throw(); + /** Finds the highest absolute sample value within a region of a channel. + */ float getMagnitude (int channel, int startSample, int numSamples) const throw(); + /** Finds the highest absolute sample value within a region on all channels. + */ float getMagnitude (int startSample, int numSamples) const throw(); + /** Returns the root mean squared level for a region of a channel. + */ float getRMSLevel (int channel, int startSample, 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, int startSample, int numSamples, @@ -14165,6 +28789,13 @@ public: bool useReaderLeftChan, 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, int startSample, int numSamples) const throw(); @@ -14185,14 +28816,38 @@ private: #endif // __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ /*** End of inlined file: juce_AudioSampleBuffer.h ***/ +/** + 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) @@ -14200,30 +28855,104 @@ struct JUCE_API AudioSourceChannelInfo } }; +/** + 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 ***/ +/** + 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* destStream, const String& formatName, double sampleRate, @@ -14231,40 +28960,91 @@ protected: 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. + + If numSamplesToRead < 0, it will write the entire length of the reader. + + @returns false if it can't read or write properly during the operation + */ bool writeFromAudioReader (AudioFormatReader& reader, int64 startSample, int64 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, 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: @@ -14277,33 +29057,117 @@ private: #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, @@ -14312,6 +29176,12 @@ public: 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 juce_wchar** const fileExtensions); @@ -14324,12 +29194,19 @@ private: #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(); @@ -14366,14 +29243,25 @@ public: #if JUCE_USE_CDBURNER +/** +*/ class AudioCDBurner : public ChangeBroadcaster { 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(); enum DiskState @@ -14386,36 +29274,92 @@ public: readOnlyDiskPresent /**< The drive contains a read-only disk. */ }; + /** Returns the current status of the device. + + To get informed when the drive's status changes, attach a ChangeListener to + the AudioCDBurner. + */ DiskState getDiskState() const; + /** Returns true if there's a writable disk in the drive. */ bool isDiskPresent() const; + /** Sends an eject signal to the drive. + The eject will happen asynchronously, so you can use getDiskState() and + waitUntilStateChange() to monitor its progress. + */ bool openTray(); + /** Blocks the current thread until the drive's state changes, or until the timeout expires. + @returns the device's new state + */ DiskState waitUntilStateChange (int timeOutMilliseconds); + /** Returns the set of possible write speeds that the device can handle. + These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc. + Note that if there's no media present in the drive, this value may be unavailable! + @see setWriteSpeed, getWriteSpeed + */ const Array getAvailableWriteSpeeds() const; + /** Tries to enable or disable buffer underrun safety on devices that support it. + @returns true if it's now enabled. If the device doesn't support it, this + will always return false. + */ bool setBufferUnderrunProtection (const bool shouldBeEnabled); + /** 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); + /** Receives progress callbacks during a cd-burn operation. + @see AudioCDBurner::burn() + */ 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 method. + */ virtual bool audioCDBurnProgress (float proportionComplete) = 0; }; + /** Runs the burn process. + This method will block until the operation is complete. + + @param listener the object to receive callbacks about progress + @param ejectDiscAfterwards whether to eject the disk after the burn completes + @param performFakeBurnForTesting if true, no data will actually be written to the disk + @param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or + 0 or less to mean the fastest speed. + */ const String burn (BurnProgressListener* listener, bool ejectDiscAfterwards, bool performFakeBurnForTesting, int writeSpeed); + /** If a burn operation is currently in progress, this tells it to stop + as soon as possible. + + It's also possible to stop the burn process by returning true from + BurnProgressListener::audioCDBurnProgress() + */ void abortBurn(); juce_UseDebuggingNewOperator @@ -14446,37 +29390,112 @@ private: #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 readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, 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 @@ -14528,35 +29547,103 @@ private: #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, 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 (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 @@ -14583,15 +29670,39 @@ private: #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* sourceReader, int64 subsectionStartSample, int64 subsectionLength, bool deleteSourceWhenDeleted); + /** Destructor. */ ~AudioSubsectionReader(); bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, @@ -14628,28 +29739,94 @@ private: 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. (This + number is the number of original samples that will be averaged for each + low-res sample) + @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 (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* 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, in seconds. + */ 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 startTimeSeconds, @@ -14657,9 +29834,13 @@ public: int channelNum, float verticalZoomFactor); + /** Returns true if the low res preview is fully generated. + */ bool isFullyLoaded() const throw(); + /** @internal */ bool useTimeSlice(); + /** @internal */ void timerCallback(); juce_UseDebuggingNewOperator @@ -14707,18 +29888,46 @@ private: 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. + */ explicit AudioThumbnailCache (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, 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, int64 hashCode); juce_UseDebuggingNewOperator @@ -14746,6 +29955,15 @@ private: #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: @@ -14786,6 +30004,15 @@ public: #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: @@ -14800,6 +30027,14 @@ public: 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, @@ -14829,12 +30064,22 @@ public: #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(); @@ -14867,28 +30112,86 @@ public: #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 char* 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 char* 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 char* 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 char* 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 char* 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 char* 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 char* 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, @@ -14911,6 +30214,11 @@ public: const StringPairArray& metadataValues, int qualityOptionIndex); + /** Utility function to replace the metadata in a wav file with a new set of values. + + If possible, this cheats by overwriting just the metadata region of the file, rather + than by copying the whole file again. + */ bool replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata); juce_UseDebuggingNewOperator @@ -14932,52 +30240,105 @@ public: #ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ #define __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ +/** + 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 ***/ +/** + 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 @@ -15016,112 +30377,348 @@ private: 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. There will be one channel of data for each input + channel that was enabled when the audio device was opened + (see AudioIODevice::open()) + @param numInputChannels the number of pointers to channel data in the + inputChannelData array. + @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. + There will be one channel of data for each output channel + that was enabled when the audio device was opened (see + AudioIODevice::open()) + The initial contents of the array is undefined, so the + callback function must fill all the channels with zeros if + its output is silence. Failing to do this could cause quite + an unpleasant noise! + @param numOutputChannels the number of pointers to channel data in the + outputChannelData array. + @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 numInputChannels, float** outputChannelData, int numOutputChannels, 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 synchronised 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 BigInteger in which a set bit indicates that the corresponding + input channel should be enabled + @param outputChannels a BigInteger 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 BigInteger& inputChannels, const BigInteger& 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 BigInteger getActiveOutputChannels() const = 0; + /** Returns a mask showing which of the available input channels are currently + enabled. + @see getInputChannelNames + */ virtual const BigInteger 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 ***/ +/** + 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; } + /** Sets a gain to apply to the audio data. */ void setGain (const float newGain) throw(); + /** 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 @@ -15158,28 +30755,56 @@ private: #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 @@ -15211,17 +30836,41 @@ private: #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); @@ -15260,47 +30909,121 @@ private: #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 @@ -15337,31 +31060,96 @@ private: #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); @@ -15402,46 +31190,100 @@ private: #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(); + /** Creates a copy of another filter. */ IIRFilter (const IIRFilter& other); + /** Destructor. */ ~IIRFilter(); + /** 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* samples, int numSamples) throw(); + /** Processes a single sample, without any locking or checking. + + Use this if you need fast processing of a single value, but be aware that + this isn't thread-safe in the way that processSamples() is. + */ float processSingleSampleRaw (float sample) throw(); + /** Sets the filter up to act as a low-pass filter. + */ void makeLowPass (double sampleRate, double frequency) throw(); + /** Sets the filter up to act as a high-pass filter. + */ void makeHighPass (double sampleRate, 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 (double sampleRate, double cutOffFrequency, double Q, 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 (double sampleRate, double cutOffFrequency, double Q, 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 (double sampleRate, double centreFrequency, double Q, 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 @@ -15463,15 +31305,27 @@ protected: #endif // __JUCE_IIRFILTER_JUCEHEADER__ /*** End of inlined file: juce_IIRFilter.h ***/ +/** + 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); @@ -15501,26 +31355,74 @@ private: #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 @@ -15555,22 +31457,33 @@ private: #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 @@ -15604,22 +31517,89 @@ private: class AudioDeviceManager; class Component; +/** + Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc. + + To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes() + method. Each of the objects returned can then be used to list the available + devices of that type. E.g. + @code + OwnedArray types; + myAudioDeviceManager.createAudioDeviceTypes (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 + + StringArray 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: + /** 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 wantInputNames 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 (bool wantInputNames = false) const = 0; + /** Returns the name of the default device. + + This will be one of the names from the getDeviceNames() list. + + @param forInput if true, this means that a default input device should be + returned; if false, it should return the default output + */ virtual int getDefaultDeviceIndex (bool forInput) const = 0; + /** Returns the index of a given device in the list of device names. + If asInput is true, it shows the index in the inputs list, otherwise it + looks for it in the outputs list. + */ virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0; + /** Returns true if two different devices can be used for the input and output. + */ virtual bool hasSeparateInputsAndOutputs() 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& outputDeviceName, const String& inputDeviceName) = 0; @@ -15631,6 +31611,7 @@ public: bool useStereoPairs; }; + /** Destructor. */ virtual ~AudioIODeviceType(); protected: @@ -15656,198 +31637,676 @@ private: #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 (int byte1, int byte2, int byte3, 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 (int byte1, int byte2, 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 (int byte1, double timeStamp = 0) throw(); + /** Creates a midi message from a block of data. */ MidiMessage (const void* data, int numBytes, double timeStamp = 0); + /** 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 maxBytesToUse 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 void* data, int maxBytesToUse, int& numBytesUsed, uint8 lastStatusByte, double timeStamp = 0); + /** Creates a copy of another midi message. */ MidiMessage (const MidiMessage& other); + /** Creates a copy of another midi message, with a different timestamp. */ MidiMessage (const MidiMessage& other, double newTimeStamp); + /** Destructor. */ ~MidiMessage(); + /** Copies this message from another one. */ MidiMessage& operator= (const MidiMessage& other); + /** 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 exact meaning of this time and its units will vary, as messages are used in + a variety of different contexts. + + If you're getting the message from a midi file, this could be a time in seconds, or + a number of ticks - see MidiFile::convertTimestampTicksToSeconds(). + + If the message is being used in a MidiBuffer, it might indicate the number of + audio samples from the start of the buffer. + + If the message was created by a MidiInput, see MidiInputCallback::handleIncomingMidiMessage() + for details of the way that it initialises this value. + + @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 the notes for getTimeStamp(). + + @see addToTimeStamp, getTimeStamp + */ void setTimeStamp (double newTimestamp) throw() { timeStamp = newTimestamp; } + /** Adds a value to the message's timestamp. + + The units for the timestamp will be application-specific. + */ void addToTimeStamp (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 (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 (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. + + @param returnTrueForVelocity0 if true, then if this event is a note-on with + velocity 0, it will still be considered to be a note-on and the + method will return true. If returnTrueForVelocity0 is false, then + if this is a note-on event with velocity 0, it'll be regarded as + a note-off, and the method will return false + + @see isNoteOff, getNoteNumber, getVelocity, noteOn + */ bool isNoteOn (bool returnTrueForVelocity0 = false) 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 (int channel, int noteNumber, 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 (int channel, int noteNumber, uint8 velocity) throw(); + /** Returns true if this message is a 'key-up' event. + + If returnTrueForNoteOnVelocity0 is true, then his will also return true + for a note-on event with a velocity of 0. + + @see isNoteOn, getNoteNumber, getVelocity, noteOff + */ bool isNoteOff (bool returnTrueForNoteOnVelocity0 = true) 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 (int channel, 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 (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 (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 (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 (int channel, 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 (int channel, 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 (int channel, int noteNumber, 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 (int channel, 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 (int channel, int controllerType, 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 (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 (int channel) throw(); + /** Creates an all-controllers-off message. + + @param channel the midi channel, in the range 1 to 16 + */ static const MidiMessage allControllersOff (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; + /** 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 (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 (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 (int numerator, int denominator); + /** 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 (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 (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 (int sequenceNumber, int value) throw(); + /** SMPTE timecode types. + + Used by the getFullFrameParameters() and fullFrame() methods. + */ enum SmpteTimecodeType { fps24 = 0, @@ -15856,20 +32315,33 @@ public: 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 (int hours, int minutes, int seconds, int frames, SmpteTimecodeType timecodeType); + /** Types of MMC command. + + @see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand + */ enum MidiMachineControlCommand { mmc_stop = 1, @@ -15882,45 +32354,121 @@ public: 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 (float volume); + /** 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, int dataSize); + /** 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 @@ -15942,14 +32490,45 @@ private: 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, @@ -15960,30 +32539,89 @@ public: } }; +/** + 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 || JUCE_MAC || DOXYGEN + /** This will try to create a new midi input device (Not available on Windows). + + 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. */ virtual ~MidiInput(); + /** Returns the name of this device. + */ virtual 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. + */ virtual 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 + */ virtual void start(); + /** Stops the device running. + + @see start + */ virtual void stop(); juce_UseDebuggingNewOperator @@ -16012,62 +32650,182 @@ private: #ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ #define __JUCE_MIDIBUFFER_JUCEHEADER__ +/** + 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 { public: + /** Creates an empty MidiBuffer. */ MidiBuffer() throw(); + /** Creates a MidiBuffer containing a single midi message. */ explicit MidiBuffer (const MidiMessage& message) throw(); + /** Creates a copy of another MidiBuffer. */ MidiBuffer (const MidiBuffer& other) throw(); + /** Makes a copy of another MidiBuffer. */ 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(); + /** Exchanges the contents of this buffer with another one. + + This is a quick operation, because no memory allocating or copying is done, it + just swaps the internal state of the two buffers. + */ void swapWith (MidiBuffer& other); + /** Preallocates some memory for the buffer to use. + This helps to avoid needing to reallocate space when the buffer has messages + added to it. + */ void ensureSize (size_t minimumNumBytes); + /** + 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(); @@ -16099,40 +32857,113 @@ private: #endif // __JUCE_MIDIBUFFER_JUCEHEADER__ /*** End of inlined file: juce_MidiBuffer.h ***/ +/** + 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 || JUCE_MAC || DOXYGEN + /** This will try to create a new midi output device (Not available on Windows). + + 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. */ virtual ~MidiOutput(); + /** Makes this device output a midi message. + + @see MidiMessage + */ virtual void sendMessageNow (const MidiMessage& message); + /** Sends a midi reset to the device. */ virtual void reset(); + /** Returns the current volume setting for this device. */ virtual bool getVolume (float& leftVol, float& rightVol); + /** Changes the overall volume for this device. */ virtual 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. + */ virtual void sendBlockOfMessages (const MidiBuffer& buffer, double millisecondCounterToStartAt, double samplesPerSecondForBuffer); + /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). + */ virtual void clearAllPendingMessages(); + /** Starts up a background thread so that the device can send blocks of data. + + Call this to get the device ready, before using sendBlockOfMessages(). + */ virtual void startBackgroundThread(); + /** Stops the background thread, and clears any pending midi events. + + @see startBackgroundThread + */ virtual void stopBackgroundThread(); juce_UseDebuggingNewOperator @@ -16204,18 +33035,42 @@ private: #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; } @@ -16231,18 +33086,59 @@ protected: #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 + */ explicit TooltipWindow (Component* parentComponent = 0, int millisecondsBeforeTipAppears = 700); + /** Destructor. */ ~TooltipWindow(); + /** Changes the time before the tip appears. + This lets you change the value that was set in the constructor. + */ void setMillisecondsBeforeTipAppears (int newTimeMs = 700) throw(); + /** 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. */ @@ -16279,16 +33175,33 @@ private: 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, @@ -16297,67 +33210,234 @@ class JUCE_API Button : public Component, { 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) + */ explicit Button (const String& buttonName); public: + /** Destructor. */ virtual ~Button(); + /** Changes the button's text. + + @see getButtonText + */ void setButtonText (const String& newText); + /** Returns the text displayed in the button. + + @see setButtonText + */ const String getButtonText() const { 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 (bool shouldBeOn, 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.getValue(); } + /** Returns the Value object that represents the botton's toggle state. + You can use this Value object to connect the button's state to external values or setters, + either by taking a copy of the Value, or by using Value::referTo() to make it point to + your own Value object. + @see getToggleState, Value + */ Value& getToggleStateValue() { 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 (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 (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* newListener); + /** Removes a previously-registered button listener + + @see addButtonListener + */ void removeButtonListener (ButtonListener* listener); + /** 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, int commandID, 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 + setCommandToTrigger() method to invoke a command. + + @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; + /** 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 (int initialDelayInMillisecs, int repeatDelayInMillisecs, 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 (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, @@ -16366,18 +33446,43 @@ public: 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 (int connectedEdgeFlags); + /** 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, @@ -16385,39 +33490,99 @@ public: 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 (bool isKeyDown, 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(); + /** @internal */ void valueChanged (Value& value); private: @@ -16466,72 +33631,244 @@ private: 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, 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 (bool isVertical, 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 (bool shouldBeVertical); + /** Shows or hides the scrollbar's buttons. */ void setButtonVisibility (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. + @see autoHides() + */ void setAutoHide (bool shouldHideWhenFullRange); + /** Returns true if this scrollbar is set to auto-hide when its thumb is as big + as its maximum range. + @see setAutoHide + */ bool autoHides() const throw(); + /** Sets the minimum and maximum values that the bar will move between. + + The bar's thumb will always be constrained so that the entire thumb lies + within this range. + + @see setCurrentRange + */ void setRangeLimits (const Range& newRangeLimit); + /** Sets the minimum and maximum values that the bar will move between. + + The bar's thumb will always be constrained so that the entire thumb lies + within this range. + + @see setCurrentRange + */ void setRangeLimits (double minimum, double maximum); + /** Returns the current limits on the thumb position. + @see setRangeLimits + */ const Range getRangeLimit() const throw() { return totalRange; } + /** Returns the lower value that the thumb can be set to. + + This is the value set by setRangeLimits(). + */ double getMinimumRangeLimit() const throw() { return totalRange.getStart(); } + /** Returns the upper value that the thumb can be set to. + + This is the value set by setRangeLimits(). + */ double getMaximumRangeLimit() const throw() { return totalRange.getEnd(); } + /** Changes the position of the scrollbar's 'thumb'. + + 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 getCurrentRange. setCurrentRangeStart + */ void setCurrentRange (const Range& newRange); + /** 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); + /** 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); + /** Returns the current thumb range. + @see getCurrentRange, setCurrentRange + */ const Range getCurrentRange() const throw() { return visibleRange; } + /** Returns the position of the top of the thumb. + @see getCurrentRange, setCurrentRangeStart + */ double getCurrentRangeStart() const throw() { return visibleRange.getStart(); } + /** Returns the current size of the thumb. + @see getCurrentRange, setCurrentRange + */ double getCurrentRangeSize() const throw() { return visibleRange.getLength(); } + /** 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 (double newSingleStepSize); + /** 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 (int howManySteps); + /** 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 (int howManyPages); + /** Scrolls to the top (or left). + + This is the same as calling setCurrentRangeStart (getMinimumRangeLimit()); + */ void scrollToTop(); + /** Scrolls to the bottom (or right). + + This is the same as calling setCurrentRangeStart (getMaximumRangeLimit() - getCurrentRangeSize()); + */ void scrollToBottom(); + /** 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 (int initialDelayInMillisecs, int repeatDelayInMillisecs, int minimumDelayInMillisecs = -1); + /** 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. */ @@ -16539,18 +33876,29 @@ public: 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* listener); + /** Deregisters a previously-registered listener. */ void removeListener (ScrollBarListener* listener); + /** @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 @@ -16578,69 +33926,211 @@ private: #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. + */ explicit 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* 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 (int xPixelsOffset, 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 (double proportionX, double proportionY); + /** If the specified position is at the edges of the viewport, this method scrolls + the viewport to bring that position nearer to the centre. + + Call this if you're dragging an object inside a viewport and want to make it scroll + when the user approaches an edge. You might also find Component::beginDragAutoRepeat() + useful when auto-scrolling. + + @param mouseX the x position, relative to the Viewport's top-left + @param mouseY the y position, relative to the Viewport's top-left + @param distanceFromEdge specifies how close to an edge the position needs to be + before the viewport should scroll in that direction + @param maximumSpeed the maximum number of pixels that the viewport is allowed + to scroll by. + @returns true if the viewport was scrolled + */ bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed); + /** Returns the position within the child component of the top-left of its visible area. + */ const Point getViewPosition() const throw() { return lastViewPos.getPosition(); } + /** Returns the position within the child component of the top-left of its visible area. + @see getViewWidth, setViewPosition + */ int getViewPositionX() const throw() { return lastViewPos.getX(); } + /** Returns the position within the child component of the top-left of its visible area. + @see getViewHeight, setViewPosition + */ int getViewPositionY() const throw() { return lastViewPos.getY(); } + /** 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 lastViewPos.getWidth(); } + /** 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 lastViewPos.getHeight(); } + /** 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 (bool showVerticalScrollbarIfNeeded, 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 (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 (int stepX, int stepY); + /** Shows or hides the buttons on any scrollbars that are used. + + @see ScrollBar::setButtonVisibility + */ void setScrollBarButtonVisibility (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, 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: @@ -16669,30 +34159,113 @@ private: class PopupMenuCustomComponent; +/** 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(); + /** Creates a copy of another menu. */ PopupMenu (const PopupMenu& other); + /** Destructor. */ ~PopupMenu(); + /** Copies this menu from another one. */ PopupMenu& operator= (const PopupMenu& other); + /** Resets the menu, removing all its items. */ void clear(); + /** 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 (int itemResultId, const String& itemText, bool isActive = true, bool isTicked = false, const Image* iconToUse = 0); + /** 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, int commandID, const String& displayName = String::empty); + /** 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 (int itemResultId, const String& itemText, const Colour& itemTextColour, @@ -16700,34 +34273,116 @@ public: bool isTicked = false, const Image* iconToUse = 0); + /** 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 (int itemResultId, PopupMenuCustomComponent* customComponent); + /** 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 (int itemResultId, Component* customComponent, int idealWidth, int idealHeight, bool triggerMenuItemAutomaticallyWhenClicked); + /** 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, bool isActive = true, Image* iconToUse = 0, bool isTicked = false); + /** 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(); + /** 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); + /** 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 (int commandID) const; + /** 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 (int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, 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. + + Note that the co-ordinates don't specify the top-left of the menu - they + indicate a point of interest, and the menu will position itself nearby to + this point, trying to keep it fully on-screen. + + @see show() + */ int showAt (int screenX, int screenY, int itemIdThatMustBeVisible = 0, @@ -16735,16 +34390,40 @@ public: int maximumNumColumns = 0, 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, int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, 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(); + /** 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* newLookAndFeel); + /** 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. */ @@ -16758,14 +34437,33 @@ public: 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); + /** Destructor. */ ~MenuItemIterator(); + /** Returns true if there is another item, and sets up all this object's + member variables to reflect that item's properties. + */ bool next(); String itemName; @@ -16838,22 +34536,42 @@ private: #ifndef __JUCE_TEXTINPUTTARGET_JUCEHEADER__ #define __JUCE_TEXTINPUTTARGET_JUCEHEADER__ +/** An abstract base class that is implemented by components that wish to be used + as text editors. + + This class allows different types of text editor component to provide a uniform + interface, which can be used by things like OS-specific input methods, on-screen + keyboards, etc. +*/ class JUCE_API TextInputTarget { public: + /** */ TextInputTarget() {} + /** Destructor. */ virtual ~TextInputTarget() {} + /** Returns true if this input target is currently accepting input. + For example, a text editor might return false if it's in read-only mode. + */ virtual bool isTextInputActive() const = 0; + /** Returns the extents of the selected text region, or an empty range if + nothing is selected, + */ virtual const Range getHighlightedRegion() const = 0; + /** Sets the currently-selected text region. + */ virtual void setHighlightedRegion (const Range& newRange) = 0; + /** Returns a specified sub-section of the text. + */ virtual const String getTextInRange (const Range& range) const = 0; + /** Inserts some text, overwriting the selected text region, if there is one. */ virtual void insertTextAtCaret (const String& textToInsert) = 0; }; @@ -16862,66 +34580,189 @@ public: class TextEditor; +/** + 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 TextInputTarget, 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). + */ explicit TextEditor (const String& componentName = String::empty, juce_wchar 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 (bool shouldBeMultiLine, bool shouldWordWrap = true); + /** Returns true if the editor is in multi-line mode. + */ bool isMultiLine() const; + /** 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 (bool shouldStartNewLine); + /** Returns the value set by setReturnKeyStartsNewLine(). + + See setReturnKeyStartsNewLine() for more info. + */ bool getReturnKeyStartsNewLine() const { 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 (bool shouldTabKeyBeUsed); + /** Returns true if the tab key is being used for input. + @see setTabKeyUsedAsCharacter + */ bool isTabKeyUsedAsCharacter() const { 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 (bool shouldBeReadOnly); + /** Returns true if the editor is in read-only mode. + */ bool isReadOnly() const; + /** Makes the caret visible or invisible. + + By default the caret is visible. + + @see setCaretColour, setCaretPosition + */ void setCaretVisible (bool shouldBeVisible); + /** Returns true if the caret is enabled. + @see setCaretVisible + */ bool isCaretVisible() const { 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); + /** Returns true if scrollbars are enabled. + @see setScrollbarsShown + */ bool areScrollbarsShown() const { 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 (juce_wchar passwordCharacter); + /** Returns the current password character. + @see setPasswordCharacter + */ juce_wchar getPasswordCharacter() const { 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 (bool menuEnabled); + /** Returns true if the right-click menu is enabled. + @see setPopupMenuEnabled + */ bool isPopupMenuEnabled() const { return popupMenuEnabled; } + /** Returns true if a popup-menu is currently being displayed. + */ bool isPopupMenuCurrentlyActive() const { 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 @@ -16950,119 +34791,343 @@ public: 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); + /** 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; + /** 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 (bool b); + /** 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 (int maxTextLength, const String& allowedCharacters = String::empty); + /** 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); + /** Changes the size of the scrollbars that are used. + + Handy if you need smaller scrollbars for a small text box. + */ void setScrollBarThickness (int newThicknessPixels); + /** Shows or hides the buttons on any scrollbars that are used. + + @see ScrollBar::setButtonVisibility + */ void setScrollBarButtonVisibility (bool buttonsVisible); + /** Registers a listener to be told when things happen to the text. + + @see removeListener + */ void addListener (TextEditorListener* newListener); + /** Deregisters a listener. + + @see addListener + */ void removeListener (TextEditorListener* listenerToRemove); + /** Returns the entire contents of the editor. */ const String getText() const; + /** Returns a section of the contents of the editor. */ const String getTextInRange (const Range& textRange) const; + /** Returns true if there are no characters in the editor. + + This is more efficient than calling getText().isEmpty(). + */ bool isEmpty() const; + /** 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, bool sendTextChangeMessage = true); + /** Returns a Value object that can be used to get or set the text. + + Bear in mind that this operate quite slowly if your text box contains large + amounts of text, as it needs to dynamically build the string that's involved. It's + best used for small text boxes. + */ Value& getTextValue(); + /** 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 insertTextAtCaret (const 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 (int newIndex); + /** Returns the current index of the caret. + + @see setCaretPosition + */ int getCaretPosition() const; + /** Attempts to scroll the text editor so that the caret ends up at + a specified position. + + This won't affect the caret's position within the text, it tries to scroll + the entire editor vertically and horizontally so that the caret is sitting + at the given position (relative to the top-left of this component). + + Depending on the amount of text available, it might not be possible to + scroll far enough for the caret to reach this exact position, but it + will go as far as it can in that direction. + */ void scrollEditorToPositionCaret (int desiredCaretX, int desiredCaretY); + /** Get the graphical position of the caret. + + The rectangle returned is relative to the component's top-left corner. + @see scrollEditorToPositionCaret + */ const Rectangle getCaretRectangle(); + /** Selects a section of the text. */ void setHighlightedRegion (const Range& newSelection); + /** Returns the range of characters that are selected. + If nothing is selected, this will return an empty range. + @see setHighlightedRegion + */ const Range getHighlightedRegion() const { return selection; } + /** Returns the section of text that is currently selected. */ const String getHighlightedText() const; + /** Finds the index of the character at a given position. + + The co-ordinates are relative to the component's top-left. + */ int getTextIndexAt (int x, int y); + /** 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() const; + /** 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; + /** 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; + /** 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 (int newLeftIndent, int newTopIndent); + /** Changes the size of border left around the edge of the component. + + @see getBorder + */ void setBorder (const BorderSize& border); + /** Returns the size of border around the edge of the component. + + @see setBorder + */ const BorderSize getBorder() const; + /** 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 (bool shouldScrollToShowCursor); + /** @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 (bool isKeyDown); + /** @internal */ void focusGained (FocusChangeType cause); + /** @internal */ void focusLost (FocusChangeType cause); + /** @internal */ void resized(); + /** @internal */ void enablementChanged(); + /** @internal */ void colourChanged(); + /** @internal */ bool isTextInputActive() const; 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 (int menuItemID); + /** Scrolls the minimum distance needed to get the caret into view. */ void scrollToMakeSureCursorIsVisible(); + /** @internal */ void moveCaret (int newCaretPos); + /** @internal */ void moveCursorTo (int newPosition, bool isSelecting); + /** Used internally to dispatch a text-change message. */ void textChanged(); + /** Begins a new transaction in the UndoManager. + */ void newTransaction(); + /** Used internally to trigger an undo or redo. */ void doUndoRedo (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: @@ -17152,14 +35217,31 @@ private: 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, @@ -17168,22 +35250,65 @@ class JUCE_API Label : public Component, { 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, 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 (bool returnActiveEditorContents = false) const throw(); + /** Returns the text content as a Value object. + You can call Value::referTo() on this object to make the label read and control + a Value object that you supply. + */ Value& getTextValue() { return textValue; } + /** 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. */ @@ -17192,77 +35317,183 @@ public: 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; } + /** Changes the gap that is left between the edge of the component and the text. + By default there's a small gap left at the sides of the component to allow for + the drawing of the border, but you can change this if necessary. + */ void setBorderSize (int horizontalBorder, int verticalBorder); + /** Returns the size of the horizontal gap being left around the text. + */ int getHorizontalBorderSize() const throw() { return horizontalBorderSize; } + /** Returns the size of the vertical gap being left around the text. + */ int getVerticalBorderSize() const throw() { return verticalBorderSize; } + /** 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, 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; + /** 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; } + /** Specifies the minimum amount that the font can be squashed horizantally before it starts + using ellipsis. + + @see Graphics::drawFittedText + */ void setMinimumHorizontalScale (float newScale); float getMinimumHorizontalScale() const throw() { return minimumHorizontalScale; } + /** Registers a listener that will be called when the label's text changes. */ void addListener (LabelListener* listener) throw(); + /** Deregisters a previously-registered listener. */ void removeListener (LabelListener* 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 (bool editOnSingleClick, bool editOnDoubleClick = false, 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 (bool discardCurrentEditorContents); + /** Returns true if the editor is currently focused and active. */ bool isBeingEdited() const throw(); juce_UseDebuggingNewOperator protected: + /** 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(); + /** Called when the text has been altered. + */ virtual void textWasChanged(); + /** Called when the text editor has just appeared, due to a user click or other + focus change. + */ virtual void editorShown (TextEditor* editorComponent); + /** Called when the text editor is going to be deleted, after editing has finished. + */ virtual void editorAboutToBeHidden (TextEditor* editorComponent); + /** @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 colourChanged(); + /** @internal */ void valueChanged (Value&); private: @@ -17292,14 +35523,39 @@ private: 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, @@ -17308,74 +35564,250 @@ class JUCE_API ComboBox : public Component, { 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()) + */ explicit 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 (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, 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 (int itemId, bool shouldBeEnabled) throw(); + /** Changes the text for an existing item. + */ void changeItemText (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 (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 (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 (int index) const throw(); + /** Returns the index in the list of a particular item ID. + If no such ID is found, this will return -1. + */ int indexOfItemId (int itemId) 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(); + /** Returns a Value object that can be used to get or set the selected item's ID. + + You can call Value::referTo() on this object to make the combo box control + another Value object. + */ Value& getSelectedIdAsValue() throw() { return currentId; } + /** 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 (int newItemId, 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 (int newItemIndex, 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, 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* listener) throw(); + /** Deregisters a previously-registered listener. */ void removeListener (ComboBoxListener* 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. */ @@ -17385,21 +35817,37 @@ public: 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 (bool isKeyDown); + /** @internal */ bool keyPressed (const KeyPress&); + /** @internal */ void valueChanged (Value&); juce_UseDebuggingNewOperator @@ -17435,36 +35883,142 @@ private: #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 addAudioCallback() 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(); + /** + This structure holds a set of properties describing the current audio setup. + + @see AudioDeviceManager::setAudioDeviceSetup() + */ struct JUCE_API AudioDeviceSetup { AudioDeviceSetup(); bool operator== (const AudioDeviceSetup& other) const; + /** The name of the audio device used for output. + The name has to be one of the ones listed by the AudioDeviceManager's currently + selected device type. + This may be the same as the input device. + */ String outputDeviceName; + /** The name of the audio device used for input. + This may be the same as the output device. + */ String inputDeviceName; + /** The current sample rate. + This rate is used for both the input and output devices. + */ double sampleRate; + /** The buffer size, in samples. + This buffer size is used for both the input and output devices. + */ int bufferSize; + /** The set of active input channels. + The bits that are set in this array indicate the channels of the + input device that are active. + */ BigInteger inputChannels; + /** If this is true, it indicates that the inputChannels array + should be ignored, and instead, the device's default channels + should be used. + */ bool useDefaultInputChannels; + /** The set of active output channels. + The bits that are set in this array indicate the channels of the + input device that are active. + */ BigInteger outputChannels; + /** If this is true, it indicates that the outputChannels array + should be ignored, and instead, the device's default channels + should be used. + */ bool useDefaultOutputChannels; }; + /** 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 + @param preferredSetupOptions if this is non-null, the structure will be used as the + set of preferred settings when opening the device. If you + use this parameter, the preferredDefaultDeviceName + field will be ignored + + @returns an error message if anything went wrong, or an empty string if it worked ok. + */ const String initialise (int numInputChannelsNeeded, int numOutputChannelsNeeded, const XmlElement* savedState, @@ -17472,57 +36026,227 @@ public: const String& preferredDefaultDeviceName = String::empty, const AudioDeviceSetup* preferredSetupOptions = 0); + /** 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 the current device properties that are in use. + + @see setAudioDeviceSetup + */ void getAudioDeviceSetup (AudioDeviceSetup& setup); + /** Changes the current device or its settings. + + If you want to change a device property, like the current sample rate or + block size, you can call getAudioDeviceSetup() to retrieve the current + settings, then tweak the appropriate fields in the AudioDeviceSetup structure, + and pass it back into this method to apply the new settings. + + @param newSetup the settings that you'd like to use + @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. + + @see getAudioDeviceSetup + */ const String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice); + /** Returns the currently-active audio device. */ AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } + /** Returns the type of audio device currently in use. + @see setCurrentAudioDeviceType + */ const String getCurrentAudioDeviceType() const { return currentDeviceType; } + /** Returns the currently active audio device type object. + Don't keep a copy of this pointer - it's owned by the device manager and could + change at any time. + */ AudioIODeviceType* getCurrentDeviceTypeObject() const; + /** Changes the class of audio device being used. + + This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call + this because there's only one type: CoreAudio. + + For a list of types, see getAvailableDeviceTypes(). + */ void setCurrentAudioDeviceType (const String& type, 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(); + /** Registers an audio callback to be used. + + The manager will redirect callbacks from whatever audio device is currently + in use to all registered callback objects. If more than one callback is + active, they will all be given the same input data, and their outputs will + be summed. + + If necessary, this method will invoke audioDeviceAboutToStart() on the callback + object before returning. + + To remove a callback, use removeAudioCallback(). + */ void addAudioCallback (AudioIODeviceCallback* newCallback); + /** Deregisters a previously added callback. + + If necessary, this method will invoke audioDeviceStopped() on the callback + object before returning. + + @see addAudioCallback + */ void removeAudioCallback (AudioIODeviceCallback* callback); + /** 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, 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 (const String& midiInputDeviceName, 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 { 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; } + /** Returns a list of the types of device supported. + */ const OwnedArray & getAvailableDeviceTypes(); + /** 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. + + You can override this if your app needs to do something specific, like avoid + using DirectSound devices, etc. + */ virtual void createAudioDeviceTypes (OwnedArray & types); + /** Plays a beep through the current audio device. + + This is here to allow the audio setup UI panels to easily include a "test" + button so that the user can check where the audio is coming from. + */ void playTestSound(); + /** Turns on level-measuring. + + When enabled, the device manager will measure the peak input level + across all channels, and you can get this level by calling getCurrentInputLevel(). + + This is mainly intended for audio setup UI panels to use to create a mic + level display, so that the user can check that they've selected the right + device. + + A simple filter is used to make the level decay smoothly, but this is + only intended for giving rough feedback, and not for any kind of accurate + measurement. + */ void enableInputLevelMeasurement (bool enableMeasurement); + /** Returns the current input level. + + To use this, you must first enable it by calling enableInputLevelMeasurement(). + + See enableInputLevelMeasurement() for more info. + */ double getCurrentInputLevel() const; juce_UseDebuggingNewOperator @@ -17630,6 +36354,11 @@ private: #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: @@ -17713,26 +36442,56 @@ private: #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. */ 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 @@ -17742,57 +36501,175 @@ public: 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 (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 (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 (int index) const; + /** Returns the index of an event. */ int getIndexOf (MidiEventHolder* 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 (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 (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 (int index, 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 (int channelNumberToExtract, MidiMessageSequence& destSequence, 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 (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 (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 (int channelNumber, double time, OwnedArray& resultMessages); + /** Swaps this sequence with another one. */ void swapWith (MidiMessageSequence& other) throw(); juce_UseDebuggingNewOperator + /** @internal */ static int compareElements (const MidiMessageSequence::MidiEventHolder* first, const MidiMessageSequence::MidiEventHolder* second) throw(); @@ -17807,43 +36684,147 @@ private: #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(); + /** Destructor. */ ~MidiFile(); + /** 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); + /** Removes all midi tracks from the file. + + @see getNumTracks + */ void clear(); + /** 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); @@ -17871,6 +36852,11 @@ private: class MidiKeyboardState; +/** + Receives events from a MidiKeyboardState object. + + @see MidiKeyboardState +*/ class JUCE_API MidiKeyboardStateListener { public: @@ -17878,13 +36864,43 @@ 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: @@ -17892,27 +36908,103 @@ 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 @@ -17944,23 +37036,62 @@ private: #ifndef __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ #define __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ +/** + 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 (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, 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 @@ -18011,15 +37142,27 @@ private: 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: @@ -18040,21 +37183,77 @@ private: 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); }; @@ -18067,6 +37266,15 @@ public: #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: @@ -18076,6 +37284,7 @@ protected: public: virtual ~AudioPlayHead() {} + /** Frame rate types. */ enum FrameRateType { fps24 = 0, @@ -18087,25 +37296,50 @@ public: 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; bool operator== (const CurrentPositionInfo& other) const throw(); @@ -18114,125 +37348,499 @@ public: void resetToDefault(); }; + /** 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 ***/ +/** + 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. + + If you're going to use this, your processBlock() method must call isSuspended() and + check whether it's suspended or not. If it is, then it should skip doing any real + processing, either emitting silence or passing the input through unchanged. + + @see getCallbackLock + */ void suspendProcessing (const bool shouldBeSuspended); + /** Returns true if processing is currently suspended. + @see suspendProcessing + */ bool isSuspended() const throw() { return suspended; } + /** A plugin can override this to be told when it should reset any playing voices. + + The default implementation does nothing, but a host may call this to tell the + plugin that it should stop any tails or sounds that have been left running. + */ virtual void reset(); + /** 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; + /** Should return true if this parameter is a "meta" parameter. + + A meta-parameter is a parameter that changes other params. It is used + by some hosts (e.g. AudioUnit hosts). + + By default this returns false. + */ virtual bool isMetaParameter (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(); @@ -18241,14 +37849,29 @@ public: 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: @@ -18275,6 +37898,16 @@ private: #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: @@ -18284,34 +37917,81 @@ public: 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; + /** Either the file containing the plugin module, or some other unique way + of identifying it. + + E.g. for an AU, this would be an ID string that the component manager + could use to retrieve the plugin. For a VST, it's the file path. + */ String fileOrIdentifier; + /** 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 @@ -18320,12 +38000,27 @@ public: #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 @@ -18342,28 +38037,75 @@ protected: 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 String& fileOrIdentifier) = 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 String& fileOrIdentifier) = 0; + /** Returns a readable version of the name of the plugin that this identifier refers to. + */ virtual const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0; + /** Checks whether this plugin could possibly be loaded. + + It doesn't actually need to load it, just to check whether the file or component + still exists. + */ virtual bool doesPluginStillExist (const PluginDescription& desc) = 0; + /** Searches a suggested set of directories for any plugins in this format. + + The path might be ignored, e.g. by AUs, which are found by the OS rather + than manually. + */ virtual const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, bool recursive) = 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 @@ -18380,6 +38122,9 @@ protected: #if JUCE_PLUGINHOST_AU && JUCE_MAC +/** + Implements a plugin format manager for AudioUnits. +*/ class JUCE_API AudioUnitPluginFormat : public AudioPluginFormat { public: @@ -18420,6 +38165,9 @@ private: // 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: @@ -18458,6 +38206,9 @@ private: // 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: @@ -18494,6 +38245,11 @@ private: #ifndef __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ #define __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ +/** Holds a set of VSTMidiEvent objects and makes it easy to add + events to the list. + + This is used by both the VST hosting code and the plugin wrapper. +*/ class VSTMidiEventList { public: @@ -18652,6 +38408,9 @@ private: #if JUCE_PLUGINHOST_VST +/** + Implements a plugin format manager for VSTs. +*/ class JUCE_API VSTPluginFormat : public AudioPluginFormat { public: @@ -18692,27 +38451,59 @@ private: #ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ #define __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ +/** + 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; + /** Checks that the file or component for this plugin actually still exists. + + (This won't try to load the plugin) + */ bool doesPluginStillExist (const PluginDescription& description) const; juce_UseDebuggingNewOperator @@ -18738,38 +38529,86 @@ private: #ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ #define __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ +/** + 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 (int index) const throw() { return types [index]; } + /** Looks for a type in the list which comes from this file. + */ PluginDescription* getTypeForFile (const String& fileOrIdentifier) 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 (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 String& possiblePluginFileOrIdentifier, bool dontRescanIfAlreadyInList, OwnedArray & typesFound, AudioPluginFormat& formatToUse); + /** 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 String& possiblePluginFileOrIdentifier) 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, @@ -18779,15 +38618,33 @@ public: 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 (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 @@ -18813,24 +38670,72 @@ private: #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, 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 (bool dontRescanIfAlreadyInList); + /** Returns the description of the plugin 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 String 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 @@ -18869,119 +38774,418 @@ private: 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); + /** You can override this to provide tool tips for specific rows. + @see TooltipClient + */ virtual const String getTooltipForRow (int row); }; +/** + 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* model); + /** Destructor. */ ~ListBox(); + /** Changes the current data model to display. */ void setModel (ListBoxModel* 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 (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 (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 (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, bool sendNotificationEventToModel = true); + /** Checks whether a row is selected. + */ bool isRowSelected (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 (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 (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 (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 (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 (int x, 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 (int x, 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 (int rowNumber, 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 (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* 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 (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. @@ -18991,33 +39195,98 @@ public: 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 (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* 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 (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 (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 (int& x, int& y); + /** 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 (bool isKeyDown); + /** @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(); + /** @internal */ void startDragAndDrop (const MouseEvent& e, const String& dragDescription); juce_UseDebuggingNewOperator @@ -19052,15 +39321,38 @@ private: #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 @@ -19073,14 +39365,25 @@ public: textColourOnId = 0x1000103 /**< The colour to use for the button's text.when the button's toggle state is "on". */ }; + /** 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 (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: @@ -19091,6 +39394,10 @@ private: #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, @@ -19099,20 +39406,37 @@ class JUCE_API PluginListComponent : public Component, { 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* 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 @@ -19151,26 +39475,65 @@ private: #ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ #define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +/** + 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 JUCE_API 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. + */ NamedValueSet properties; + /** A convenient typedef for referring to a pointer to a node object. + */ typedef ReferenceCountedObjectPtr Ptr; juce_UseDebuggingNewOperator @@ -19189,16 +39552,40 @@ public: Node& operator= (const Node&); }; + /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. + + To create a connection, use AudioProcessorGraph::addConnection(). + */ struct JUCE_API 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 @@ -19206,50 +39593,131 @@ public: 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 { 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 { 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; + /** 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* 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 (uint32 nodeId); + /** Returns the number of connections in the graph. */ int getNumConnections() const { return connections.size(); } + /** Returns a pointer to one of the connections in the graph. */ const Connection* getConnection (int index) const { return connections [index]; } + /** Searches for a connection between some specified channels. + + If no such connection is found, this returns 0. + */ const Connection* getConnectionBetween (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) const; + /** Returns true if there is a connection between any of the channels of + two specified nodes. + */ bool isConnected (uint32 possibleSourceNodeId, uint32 possibleDestNodeId) const; + /** Returns true if it would be legal to connect the specified points. + */ bool canConnect (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) const; + /** 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 false. + */ bool addConnection (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex); + /** Deletes the connection with the specified index. + + Returns true if a connection was actually deleted. + */ void removeConnection (int index); + /** Deletes any connection between two specified points. + + Returns true if a connection was actually deleted. + */ bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex); + /** Removes all connections from the specified node. + */ bool disconnectNode (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 JUCE_API AudioGraphIOProcessor : public AudioPluginInstance { public: + /** Specifies the mode in which this processor will operate. + */ enum IODeviceType { audioInputNode, /**< In this mode, the processor has output channels @@ -19266,11 +39734,16 @@ public: graph. */ }; + /** Returns the mode of this processor. */ IODeviceType getType() const { 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 { return graph; } + /** True if this is an audio or midi input. */ bool isInput() const; + /** True if this is an audio or midi output. */ bool isOutput() const; AudioGraphIOProcessor (const IODeviceType type); @@ -19307,6 +39780,7 @@ public: void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); void setStateInformation (const void* data, int sizeInBytes); + /** @internal */ void setParentGraph (AudioProcessorGraph* graph); juce_UseDebuggingNewOperator @@ -19352,6 +39826,7 @@ public: void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); void setStateInformation (const void* data, int sizeInBytes); + /** @internal */ void handleAsyncUpdate(); juce_UseDebuggingNewOperator @@ -19395,28 +39870,57 @@ private: #ifndef __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ #define __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ +/** + 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 { 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() { 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 @@ -19462,72 +39966,202 @@ private: 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 SettableTooltipClient { 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, 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; } void setPreferredHeight (int newHeight) throw() { preferredHeight = newHeight; } + /** 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 ***/ +/** + 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, 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 (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 (int sectionIndex, bool shouldBeOpen); + /** Enables or disables one of the sections. + + The index is from 0 up to the number of items returned by getSectionNames(). + */ void setSectionEnabled (int sectionIndex, bool shouldBeEnabled); + /** 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; + /** @internal */ void paint (Graphics& g); + /** @internal */ void resized(); juce_UseDebuggingNewOperator @@ -19545,6 +40179,15 @@ private: #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: @@ -19580,6 +40223,18 @@ private: #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: @@ -19587,57 +40242,166 @@ 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 { return currentlyPlayingNote; } + /** Returns the sound that this voice is currently playing. + + Returns 0 if it's not playing. + */ const SynthesiserSound::Ptr getCurrentlyPlayingSound() const { 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 (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 (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 { 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: @@ -19650,58 +40414,191 @@ private: 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 { return voices.size(); } + /** Returns one of the voices that have been added. */ SynthesiserVoice* getVoice (int index) const; + /** 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* newVoice); + /** Deletes one of the voices. */ void removeVoice (int index); + /** Deletes all sounds. */ void clearSounds(); + /** Returns the number of sounds that have been added to the synth. */ int getNumSounds() const { return sounds.size(); } + /** Returns one of the sounds. */ SynthesiserSound* getSound (int index) const { 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 (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 (bool shouldStealNotes); + /** Returns true if note-stealing is enabled. + @see setNoteStealingEnabled + */ bool isNoteStealingEnabled() const { 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, @@ -19711,22 +40608,37 @@ public: 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* voice, SynthesiserSound* sound, int midiChannel, int midiNoteNumber, 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: @@ -19741,10 +40653,39 @@ private: #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 BigInteger& midiNotes, @@ -19753,10 +40694,15 @@ public: double releaseTimeSecs, double maxSampleLengthSeconds); + /** Destructor. */ ~SamplerSound(); + /** Returns the sample's name */ const String& getName() const { return name; } + /** Returns the audio sample data. + This could be 0 if there was a problem loading it. + */ AudioSampleBuffer* getAudioData() const { return data; } bool appliesToNote (const int midiNoteNumber); @@ -19775,12 +40721,23 @@ private: 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); @@ -19826,22 +40783,51 @@ private: #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* 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* 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 @@ -19857,20 +40843,42 @@ private: #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* listener); + /** Removes a listener from the list. + + If the listener isn't on the list, this won't have any effect. + */ void removeActionListener (ActionListener* 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: @@ -19901,16 +40909,44 @@ private: #ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ #define __JUCE_CALLBACKMESSAGE_JUCEHEADER__ +/** + A message that calls a custom function when it gets delivered. + + You can use this class to fire off actions that you want to be performed later + on the message thread. + + Unlike other Message objects, these don't get sent to a MessageListener, you + just call the post() method to send them, and when they arrive, your + messageCallback() method will automatically be invoked. + + @see MessageListener, MessageManager, ActionListener, ChangeListener +*/ class JUCE_API CallbackMessage : public Message { public: CallbackMessage() throw(); + /** Destructor. */ ~CallbackMessage() throw(); + /** Called when the message is delivered. + + You should implement this method and make it do whatever action you want + to perform. + + Note that like all other messages, this object will be deleted immediately + after this method has been invoked. + */ virtual void messageCallback() = 0; + /** Instead of sending this message to a MessageListener, just call this method + to post it to the event queue. + + After you've called this, this object will belong to the MessageManager, + which will delete it later. So make sure you don't delete the object yourself, + call post() more than once, or call post() on a stack-based obect! + */ void post(); juce_UseDebuggingNewOperator @@ -19942,42 +40978,147 @@ private: 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 (bool callbacksOnMessageThread = true, 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, int portNumber, 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, 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, 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 @@ -20020,19 +41161,55 @@ private: #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 (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: @@ -20071,41 +41248,122 @@ private: class Component; class MessageManagerLock; +/** 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 { public: + /** Returns the global instance of the MessageManager. */ static MessageManager* getInstance() throw(); + /** Runs the event dispatch loop until a stop message is posted. + + This method is only intended to be run by the application's startup routine, + as it blocks, and will only return after the stopDispatchLoop() method has been used. + + @see stopDispatchLoop + */ void runDispatchLoop(); + /** Sends a signal that the dispatch loop should terminate. + + After this is called, the runDispatchLoop() or runDispatchLoopUntil() methods + will be interrupted and will return. + + @see runDispatchLoop + */ void stopDispatchLoop(); + /** Returns true if the stopDispatchLoop() method has been called. + */ bool hasStopMessageBeenSent() const throw() { return quitMessagePosted; } + /** Synchronously dispatches messages until a given time has elapsed. + + Returns false if a quit message has been posted by a call to stopDispatchLoop(), + otherwise returns true. + */ bool runDispatchLoopUntil (int millisecondsToRunFor); + /** 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 that the current 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 setCurrentThreadAsMessageThread(); + /** 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 + */ Thread::ThreadID 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(); + /** @internal */ void deliverMessage (void*); + /** @internal */ void deliverBroadcastMessage (const String&); + /** @internal */ ~MessageManager() throw(); juce_UseDebuggingNewOperator @@ -20143,16 +41401,102 @@ private: 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. + + The constructor attempts to gain a lock on the message loop, and the lock will be + kept for the lifetime of this object. + + Optionally, you can pass a thread object here, and while waiting to obtain the lock, + this method will keep checking whether the thread has been given the + Thread::signalThreadShouldExit() signal. If this happens, then it will return + without gaining the lock. If you pass a thread, you must check whether the lock was + successful by calling lockWasGained(). If this is false, your thread is being told to + die, so you should take evasive action. + + If you pass zero for the thread object, it will wait indefinitely for the lock - be + careful when doing this, because it's very easy to deadlock if your message thread + attempts to call stopThread() on a thread just as that thread attempts to get the + message lock. + + If the calling thread already has the lock, nothing will be done, so it's safe and + quick to use 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* threadToCheckForExitSignal = 0) throw(); + /** This has the same behaviour as the other constructor, but takes a ThreadPoolJob + instead of a thread. + + See the MessageManagerLock (Thread*) constructor for details on how this works. + */ MessageManagerLock (ThreadPoolJob* jobToCheckForExitSignal) 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(); + /** 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: @@ -20180,26 +41524,89 @@ private: #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 (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 (int timerId, 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 (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 (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 (int timerId) const throw(); private: @@ -20229,19 +41636,50 @@ private: #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 (float newRadius, float newOpacity, int newShadowOffsetX, int newShadowOffsetY); + /** @internal */ void applyEffect (Image& sourceImage, Graphics& destContext); juce_UseDebuggingNewOperator @@ -20254,23 +41692,38 @@ private: #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: @@ -20303,24 +41756,65 @@ private: #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. + @see drawWithin + */ void draw (Graphics& g, float opacity, const AffineTransform& transform = AffineTransform::identity) const; + /** 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, float x, float y, float opacity) 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. + @param opacity the opacity to use, in the range 0 to 1.0 + */ void drawWithin (Graphics& g, int destX, int destY, @@ -20329,6 +41823,9 @@ public: const RectanglePlacement& placement, float opacity) const; + /** Holds the information needed when telling a drawable to render itself. + @see Drawable::draw + */ class RenderingContext { public: @@ -20342,26 +41839,72 @@ public: RenderingContext& operator= (const RenderingContext&); }; + /** Renders this Drawable object. + @see draw + */ virtual void render (const RenderingContext& context) const = 0; + /** Returns the smallest rectangle that can contain this Drawable object. + + Co-ordinates are relative to the object's own origin. + */ virtual const Rectangle getBounds() 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, size_t 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); + /** Tries to create a Drawable from a previously-saved ValueTree. + The ValueTree must have been created by the createValueTree() method. + */ static Drawable* createFromValueTree (const ValueTree& tree); + /** Creates a ValueTree to represent this Drawable. + The VarTree that is returned can be turned back into a Drawable with + createFromValueTree(). + */ virtual ValueTree createValueTree() const = 0; juce_UseDebuggingNewOperator @@ -20376,6 +41919,14 @@ private: #endif // __JUCE_DRAWABLE_JUCEHEADER__ /*** End of inlined file: juce_Drawable.h ***/ +/** + 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: @@ -20389,11 +41940,49 @@ public: 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, 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, @@ -20403,15 +41992,44 @@ public: const Drawable* downImageOn = 0, const Drawable* disabledImageOn = 0); + /** Changes the button's style. + + @see ButtonStyle + */ void setButtonStyle (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 (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(); @@ -20420,6 +42038,7 @@ public: juce_UseDebuggingNewOperator protected: + /** @internal */ void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); @@ -20448,35 +42067,70 @@ private: #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, 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); @@ -20504,14 +42158,81 @@ private: #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 + */ explicit 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 (bool resizeButtonNowToFitThisImage, bool rescaleImagesWhenButtonSizeChanges, bool preserveImageProportions, @@ -20526,16 +42247,29 @@ public: const Colour& overlayColourWhenDown, 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); @@ -20569,32 +42303,65 @@ private: #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, bool resizeNowToFitThisShape, bool maintainShapeProportions, 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, float outlineStrokeWidth); juce_UseDebuggingNewOperator protected: + /** @internal */ void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); @@ -20624,16 +42391,42 @@ private: #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. */ @@ -20642,10 +42435,12 @@ public: juce_UseDebuggingNewOperator protected: + /** @internal */ void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); + /** @internal */ void colourChanged(); private: @@ -20685,62 +42480,252 @@ private: #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() + @param sourceComponent the component that was 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 that was 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 that was 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 that was 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 that was 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. + @param imageOffsetFromMouse if an image has been passed-in, this specifies the offset + at which the image should be drawn from the mouse. If it isn't + specified, then the image will be centred around the mouse. If + an image hasn't been passed-in, this will be ignored. + */ void startDragging (const String& sourceDescription, Component* sourceComponent, Image* dragImage = 0, bool allowDraggingToOtherJuceWindows = false, const Point* imageOffsetFromMouse = 0); + /** 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, 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, @@ -20760,28 +42745,94 @@ private: #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. + + The class is a ChangeBroadcaster and sends a notification when any components + start or finish being animated. +*/ class JUCE_API ComponentAnimator : public ChangeBroadcaster, 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* component, const Rectangle& finalPosition, int millisecondsToSpendMoving, double startSpeed = 1.0, 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* component, 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 (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* component); + /** Returns true if the specified component is currently being animated. + */ bool isAnimating (Component* component) const; juce_UseDebuggingNewOperator @@ -20802,6 +42853,22 @@ class ToolbarItemComponent; class ToolbarItemFactory; class MissingItemsComponent; +/** + 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, @@ -20809,34 +42876,110 @@ class JUCE_API Toolbar : public Component, { 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 (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, int itemId, int insertIndex = -1); + /** Deletes one of the items from the bar. + */ void removeToolbarItem (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 (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 (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. */ @@ -20844,10 +42987,17 @@ public: 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 @@ -20862,11 +43012,40 @@ public: 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, 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 (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 @@ -20885,20 +43064,43 @@ public: 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 @@ -20927,40 +43129,131 @@ private: 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 (int itemId, const String& labelText, 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. */ @@ -20970,11 +43263,23 @@ public: 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 @@ -20997,20 +43302,50 @@ private: #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 (int itemId, const String& labelText, Drawable* normalImage, Drawable* 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 @@ -21035,52 +43370,149 @@ private: class CodeDocumentLine; +/** + A class for storing and manipulating a source code file. + + When using a CodeEditorComponent, it takes one of these as its source object. + + The CodeDocument stores its content as an array of lines, which makes it + quick to insert and delete. + + @see CodeEditorComponent +*/ class JUCE_API CodeDocument { public: + /** Creates a new, empty document. + */ CodeDocument(); + /** Destructor. */ ~CodeDocument(); + /** A position in a code document. + + Using this class you can find a position in a code document and quickly get its + character position, line, and index. By calling setPositionMaintained (true), the + position is automatically updated when text is inserted or deleted in the document, + so that it maintains its original place in the text. + */ class JUCE_API Position { public: + /** Creates an uninitialised postion. + Don't attempt to call any methods on this until you've given it an owner document + to refer to! + */ Position() throw(); + /** Creates a position based on a line and index in a document. + + Note that this index is NOT the column number, it's the number of characters from the + start of the line. The "column" number isn't quite the same, because if the line + contains any tab characters, the relationship of the index to its visual column depends on + the number of spaces per tab being used! + + Lines are numbered from zero, and if the line or index are beyond the bounds of the document, + they will be adjusted to keep them within its limits. + */ Position (const CodeDocument* ownerDocument, int line, int indexInLine) throw(); + /** Creates a position based on a character index in a document. + This position is placed at the specified number of characters from the start of the + document. The line and column are auto-calculated. + + If the position is beyond the range of the document, it'll be adjusted to keep it + inside. + */ Position (const CodeDocument* ownerDocument, int charactersFromStartOfDocument) throw(); + /** Creates a copy of another position. + + This will copy the position, but the new object will not be set to maintain its position, + even if the source object was set to do so. + */ Position (const Position& other) throw(); + /** Destructor. */ ~Position() throw(); Position& operator= (const Position& other) throw(); bool operator== (const Position& other) const throw(); bool operator!= (const Position& other) const throw(); + /** Points this object at a new position within the document. + + If the position is beyond the range of the document, it'll be adjusted to keep it + inside. + @see getPosition, setLineAndIndex + */ void setPosition (int charactersFromStartOfDocument) throw(); + /** Returns the position as the number of characters from the start of the document. + @see setPosition, getLineNumber, getIndexInLine + */ int getPosition() const throw() { return characterPos; } + /** Moves the position to a new line and index within the line. + + Note that the index is NOT the column at which the position appears in an editor. + If the line contains any tab characters, the relationship of the index to its + visual position depends on the number of spaces per tab being used! + + Lines are numbered from zero, and if the line or index are beyond the bounds of the document, + they will be adjusted to keep them within its limits. + */ void setLineAndIndex (int newLine, int newIndexInLine) throw(); + /** Returns the line number of this position. + The first line in the document is numbered zero, not one! + */ int getLineNumber() const throw() { return line; } + /** Returns the number of characters from the start of the line. + + Note that this value is NOT the column at which the position appears in an editor. + If the line contains any tab characters, the relationship of the index to its + visual position depends on the number of spaces per tab being used! + */ int getIndexInLine() const throw() { return indexInLine; } + /** Allows the position to be automatically updated when the document changes. + + If this is set to true, the positon will register with its document so that + when the document has text inserted or deleted, this position will be automatically + moved to keep it at the same position in the text. + */ void setPositionMaintained (bool isMaintained) throw(); + /** Moves the position forwards or backwards by the specified number of characters. + @see movedBy + */ void moveBy (int characterDelta) throw(); + /** Returns a position which is the same as this one, moved by the specified number of + characters. + @see moveBy + */ const Position movedBy (int characterDelta) const throw(); + /** Returns a position which is the same as this one, moved up or down by the specified + number of lines. + @see movedBy + */ const Position movedByLines (int deltaLines) const throw(); + /** Returns the character in the document at this position. + @see getLineText + */ const juce_wchar getCharacter() const throw(); + /** Returns the line from the document that this position is within. + @see getCharacter, getLineNumber + */ const String getLineText() const throw(); private: @@ -21089,64 +43521,146 @@ public: bool positionMaintained; }; + /** Returns the full text of the document. */ const String getAllContent() const throw(); + /** Returns a section of the document's text. */ const String getTextBetween (const Position& start, const Position& end) const throw(); + /** Returns a line from the document. */ const String getLine (int lineIndex) const throw(); + /** Returns the number of characters in the document. */ int getNumCharacters() const throw(); + /** Returns the number of lines in the document. */ int getNumLines() const throw() { return lines.size(); } + /** Returns the number of characters in the longest line of the document. */ int getMaximumLineLength() throw(); + /** Deletes a section of the text. + + This operation is undoable. + */ void deleteSection (const Position& startPosition, const Position& endPosition); + /** Inserts some text into the document at a given position. + + This operation is undoable. + */ void insertText (const Position& position, const String& text); + /** Clears the document and replaces it with some new text. + + This operation is undoable - if you're trying to completely reset the document, you + might want to also call clearUndoHistory() and setSavePoint() after using this method. + */ void replaceAllContent (const String& newContent); + /** Replaces the editor's contents with the contents of a stream. + This will also reset the undo history and save point marker. + */ bool loadFromStream (InputStream& stream); + /** Writes the editor's current contents to a stream. */ bool writeToStream (OutputStream& stream); + /** Returns the preferred new-line characters for the document. + This will be either "\n", "\r\n", or (rarely) "\r". + @see setNewLineCharacters + */ const String getNewLineCharacters() const throw() { return newLineChars; } + /** Sets the new-line characters that the document should use. + The string must be either "\n", "\r\n", or (rarely) "\r". + @see getNewLineCharacters + */ void setNewLineCharacters (const String& newLine) throw(); + /** Begins a new undo transaction. + + The document itself will not call this internally, so relies on whatever is using the + document to periodically call this to break up the undo sequence into sensible chunks. + @see UndoManager::beginNewTransaction + */ void newTransaction(); + /** Undo the last operation. + @see UndoManager::undo + */ void undo(); + /** Redo the last operation. + @see UndoManager::redo + */ void redo(); + /** Clears the undo history. + @see UndoManager::clearUndoHistory + */ void clearUndoHistory(); + /** Returns the document's UndoManager */ UndoManager& getUndoManager() throw() { return undoManager; } + /** Makes a note that the document's current state matches the one that is saved. + + After this has been called, hasChangedSinceSavePoint() will return false until + the document has been altered, and then it'll start returning true. If the document is + altered, but then undone until it gets back to this state, hasChangedSinceSavePoint() + will again return false. + + @see hasChangedSinceSavePoint + */ void setSavePoint() throw(); + /** Returns true if the state of the document differs from the state it was in when + setSavePoint() was last called. + + @see setSavePoint + */ bool hasChangedSinceSavePoint() const throw(); + /** Searches for a word-break. */ const Position findWordBreakAfter (const Position& position) const throw(); + /** Searches for a word-break. */ const Position findWordBreakBefore (const Position& position) const throw(); + /** An object that receives callbacks from the CodeDocument when its text changes. + @see CodeDocument::addListener, CodeDocument::removeListener + */ class JUCE_API Listener { public: Listener() {} virtual ~Listener() {} + /** Called by a CodeDocument when it is altered. + */ virtual void codeDocumentChanged (const Position& affectedTextStart, const Position& affectedTextEnd) = 0; }; + /** Registers a listener object to receive callbacks when the document changes. + If the listener is already registered, this method has no effect. + @see removeListener + */ void addListener (Listener* listener) throw(); + /** Deregisters a listener. + @see addListener + */ void removeListener (Listener* listener) throw(); + /** Iterates the text in a CodeDocument. + + This class lets you read characters from a CodeDocument. It's designed to be used + by a SyntaxAnalyser object. + + @see CodeDocument, SyntaxAnalyser + */ class Iterator { public: @@ -21155,20 +43669,32 @@ public: Iterator& operator= (const Iterator& other) throw(); ~Iterator() throw(); + /** Reads the next character and returns it. + @see peekNextChar + */ juce_wchar nextChar(); + /** Reads the next character without advancing the current position. */ juce_wchar peekNextChar() const; + /** Advances the position by one character. */ void skip(); + /** Returns the position of the next character as its position within the + whole document. + */ int getPosition() const throw() { return position; } + /** Skips over any whitespace characters until the next character is non-whitespace. */ void skipWhitespace(); + /** Skips forward until the next character will be the first character on the next line */ void skipToEndOfLine(); + /** Returns the line number of the next character. */ int getLine() const throw() { return line; } + /** Returns true if the iterator has reached the end of the document. */ bool isEOF() const throw(); private: @@ -21219,16 +43745,35 @@ private: #ifndef __JUCE_CODETOKENISER_JUCEHEADER__ #define __JUCE_CODETOKENISER_JUCEHEADER__ +/** + A base class for tokenising code so that the syntax can be displayed in a + code editor. + + @see CodeDocument, CodeEditorComponent +*/ class JUCE_API CodeTokeniser { public: CodeTokeniser() {} virtual ~CodeTokeniser() {} + /** Reads the next token from the source and returns its token type. + + This must leave the source pointing to the first character in the + next token. + */ virtual int readNextToken (CodeDocument::Iterator& source) = 0; + /** Returns a list of the names of the token types this analyser uses. + + The index in this list must match the token type numbers that are + returned by readNextToken(). + */ virtual const StringArray getTokenTypes() = 0; + /** Returns a suggested syntax highlighting colour for a specified + token type. + */ virtual const Colour getDefaultColour (int tokenType) = 0; juce_UseDebuggingNewOperator @@ -21237,6 +43782,12 @@ public: #endif // __JUCE_CODETOKENISER_JUCEHEADER__ /*** End of inlined file: juce_CodeTokeniser.h ***/ +/** + A text editor component designed specifically for source code. + + This is designed to handle syntax highlighting and fast editing of very large + files. +*/ class JUCE_API CodeEditorComponent : public Component, public TextInputTarget, public Timer, @@ -21246,29 +43797,64 @@ class JUCE_API CodeEditorComponent : public Component, { public: + /** Creates an editor for a document. + + The tokeniser object is optional - pass 0 to disable syntax highlighting. + The object that you pass in is not owned or deleted by the editor - you must + make sure that it doesn't get deleted while this component is still using it. + + @see CodeDocument + */ CodeEditorComponent (CodeDocument& document, CodeTokeniser* codeTokeniser); + /** Destructor. */ ~CodeEditorComponent(); + /** Returns the code document that this component is editing. */ CodeDocument& getDocument() const throw() { return document; } + /** Loads the given content into the document. + This will completely reset the CodeDocument object, clear its undo history, + and fill it with this text. + */ void loadContent (const String& newContent); + /** Returns the standard character width. */ float getCharWidth() const throw() { return charWidth; } + /** Returns the height of a line of text, in pixels. */ int getLineHeight() const throw() { return lineHeight; } + /** Returns the number of whole lines visible on the screen, + This doesn't include a cut-off line that might be visible at the bottom if the + component's height isn't an exact multiple of the line-height. + */ int getNumLinesOnScreen() const throw() { return linesOnScreen; } + /** Returns the number of whole columns visible on the screen. + This doesn't include any cut-off columns at the right-hand edge. + */ int getNumColumnsOnScreen() const throw() { return columnsOnScreen; } + /** Returns the current caret position. */ const CodeDocument::Position getCaretPos() const { return caretPos; } + /** Moves the caret. + If selecting is true, the section of the document between the current + caret position and the new one will become selected. If false, any currently + selected region will be deselected. + */ void moveCaretTo (const CodeDocument::Position& newPos, bool selecting); + /** Returns the on-screen position of a character in the document. + The rectangle returned is relative to this component's top-left origin. + */ const Rectangle getCharacterBounds (const CodeDocument::Position& pos) const throw(); + /** Finds the character at a given on-screen position. + The co-ordinates are relative to this component's top-left origin. + */ const CodeDocument::Position getPositionAt (int x, int y); void cursorLeft (bool moveInWholeWordSteps, bool selecting); @@ -21310,23 +43896,58 @@ public: void setHighlightedRegion (const Range& newRange); const String getTextInRange (const Range& range) const; + /** Changes the current tab settings. + This lets you change the tab size and whether pressing the tab key inserts a + tab character, or its equivalent number of spaces. + */ void setTabSize (int numSpacesPerTab, bool insertSpacesInsteadOfTabCharacters) throw(); + /** Returns the current number of spaces per tab. + @see setTabSize + */ int getTabSize() const throw() { return spacesPerTab; } + /** Returns true if the tab key will insert spaces instead of actual tab characters. + @see setTabSize + */ bool areSpacesInsertedForTabs() const { return useSpacesForTabs; } + /** Changes the font. + Make sure you only use a fixed-width font, or this component will look pretty nasty! + */ void setFont (const Font& newFont); + /** Returns the font that the editor is using. */ const Font& getFont() const throw() { return font; } + /** Resets the syntax highlighting colours to the default ones provided by the + code tokeniser. + @see CodeTokeniser::getDefaultColour + */ void resetToDefaultColours(); + /** Changes one of the syntax highlighting colours. + The token type values are dependent on the tokeniser being used - use + CodeTokeniser::getTokenTypes() to get a list of the token types. + @see getColourForTokenType + */ void setColourForTokenType (int tokenType, const Colour& colour); + /** Returns one of the syntax highlighting colours. + The token type values are dependent on the tokeniser being used - use + CodeTokeniser::getTokenTypes() to get a list of the token types. + @see setColourForTokenType + */ const Colour getColourForTokenType (int tokenType) const throw(); + /** 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 = 0x1004500, /**< A colour to use to fill the editor's background. */ @@ -21337,25 +43958,42 @@ public: enabled. */ }; + /** Changes the size of the scrollbars. */ void setScrollbarThickness (int thickness) throw(); + /** Returns the thickness of the scrollbars. */ int getScrollbarThickness() const throw() { return scrollbarThickness; } + /** @internal */ void resized(); + /** @internal */ void paint (Graphics& g); + /** @internal */ bool keyPressed (const KeyPress& key); + /** @internal */ void mouseDown (const MouseEvent& e); + /** @internal */ void mouseDrag (const MouseEvent& e); + /** @internal */ void mouseUp (const MouseEvent& e); + /** @internal */ void mouseDoubleClick (const MouseEvent& e); + /** @internal */ void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + /** @internal */ void focusGained (FocusChangeType cause); + /** @internal */ void focusLost (FocusChangeType cause); + /** @internal */ void timerCallback(); + /** @internal */ void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart); + /** @internal */ void handleAsyncUpdate(); + /** @internal */ void codeDocumentChanged (const CodeDocument::Position& affectedTextStart, const CodeDocument::Position& affectedTextEnd); + /** @internal */ bool isTextInputActive() const; juce_UseDebuggingNewOperator @@ -21426,6 +44064,11 @@ private: #ifndef __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__ #define __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__ +/** + A simple lexical analyser for syntax colouring of C++ code. + + @see SyntaxAnalyser, CodeEditorComponent, CodeDocument +*/ class JUCE_API CPlusPlusCodeTokeniser : public CodeTokeniser { public: @@ -21475,20 +44118,59 @@ public: #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! + */ explicit 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. */ @@ -21499,9 +44181,13 @@ public: juce_UseDebuggingNewOperator protected: + /** @internal */ void paint (Graphics& g); + /** @internal */ void lookAndFeelChanged(); + /** @internal */ void visibilityChanged(); + /** @internal */ void colourChanged(); private: @@ -21535,22 +44221,72 @@ private: 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, @@ -21560,10 +44296,20 @@ class JUCE_API Slider : public Component, { public: + /** Creates a slider. + + When created, you'll need to set up the slider's style and range with setSliderStyle(), + setRange(), etc. + */ explicit Slider (const String& componentName); + /** Destructor. */ ~Slider(); + /** The types of slider available. + + @see setSliderStyle, setRotaryParameters + */ enum SliderStyle { LinearHorizontal, /**< A traditional horizontal slider. */ @@ -21590,39 +44336,131 @@ public: @see setMinValue, setMaxValue */ }; + /** Changes the type of slider interface being used. + + @param newStyle the type of interface + @see setRotaryParameters, setVelocityBasedMode, + */ void setSliderStyle (SliderStyle newStyle); + /** Returns the slider's current style. + + @see setSliderStyle + */ SliderStyle getSliderStyle() const { 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 (float startAngleRadians, float endAngleRadians, 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 (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 (bool isVelocityBased); + /** Returns true if velocity-based mode is active. + @see setVelocityBasedMode + */ bool getVelocityBasedMode() const { return isVelocityBased; } + /** 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 + @param userCanPressKeyToSwapMode if true, then the user can hold down the ctrl or command + key to toggle velocity-sensitive mode + */ void setVelocityModeParameters (double sensitivity = 1.0, int threshold = 1, double offset = 0.0, bool userCanPressKeyToSwapMode = true); + /** Returns the velocity sensitivity setting. + @see setVelocityModeParameters + */ double getVelocitySensitivity() const { return velocityModeSensitivity; } + /** Returns the velocity threshold setting. + @see setVelocityModeParameters + */ int getVelocityThreshold() const { return velocityModeThreshold; } + /** Returns the velocity offset setting. + @see setVelocityModeParameters + */ double getVelocityOffset() const { return velocityModeOffset; } + /** Returns the velocity user key setting. + @see setVelocityModeParameters + */ bool getVelocityModeIsSwappable() const { return userKeyOverridesVelocity; } + /** 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 (double factor); + /** 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 (double sliderValueToShowAtMidPoint); + /** Returns the current skew factor. + + See setSkewFactor for more info. + + @see setSkewFactor, setSkewFactorFromMidPoint + */ double getSkewFactor() const { return skewFactor; } + /** Used by setIncDecButtonsMode(). + */ enum IncDecButtonMode { incDecButtonsNotDraggable, @@ -21631,8 +44469,22 @@ public: 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 (IncDecButtonMode mode); + /** The position of the slider's text-entry box. + + @see setTextBoxStyle + */ enum TextEntryBoxPosition { NoTextBox, /**< Doesn't display a text box. */ @@ -21642,87 +44494,308 @@ public: 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 (TextEntryBoxPosition newPosition, bool isReadOnly, int textEntryBoxWidth, int textEntryBoxHeight); + /** Returns the status of the text-box. + @see setTextBoxStyle + */ const TextEntryBoxPosition getTextBoxPosition() const { return textBoxPos; } + /** Returns the width used for the text-box. + @see setTextBoxStyle + */ int getTextBoxWidth() const { return textBoxWidth; } + /** Returns the height used for the text-box. + @see setTextBoxStyle + */ int getTextBoxHeight() const { 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 (bool shouldBeEditable); + /** Returns true if the text-box is read-only. + @see setTextBoxStyle + */ bool isTextBoxEditable() const { 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 (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, bool sendUpdateMessage = true, bool sendMessageSynchronously = false); + /** Returns the slider's current value. */ double getValue() const; + /** Returns the Value object that represents the slider's current position. + You can use this Value object to connect the slider's position to external values or setters, + either by taking a copy of the Value, or by using Value::referTo() to make it point to + your own Value object. + @see Value, getMaxValue, getMinValueObject + */ Value& getValueObject() { return currentValue; } + /** 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 (double newMinimum, double newMaximum, double newInterval = 0); + /** Returns the current maximum value. + @see setRange + */ double getMaximum() const { return maximum; } + /** Returns the current minimum value. + @see setRange + */ double getMinimum() const { return minimum; } + /** Returns the current step-size for values. + @see setRange + */ double getInterval() const { 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; + /** For a slider with two or three thumbs, this returns the lower of its values. + You can use this Value object to connect the slider's position to external values or setters, + either by taking a copy of the Value, or by using Value::referTo() to make it point to + your own Value object. + @see Value, getMinValue, getMaxValueObject + */ Value& getMinValueObject() { return valueMin; } + /** 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 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 + @param allowNudgingOfOtherValues if false, this value will be restricted to being below the + max value (in a two-value slider) or the mid value (in a three-value + slider). If false, then if this value goes beyond those values, + it will push them along with it. + @see getMinValue, setMaxValue, setValue + */ void setMinValue (double newValue, bool sendUpdateMessage = true, bool sendMessageSynchronously = false, bool allowNudgingOfOtherValues = 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; + /** For a slider with two or three thumbs, this returns the higher of its values. + You can use this Value object to connect the slider's position to external values or setters, + either by taking a copy of the Value, or by using Value::referTo() to make it point to + your own Value object. + @see Value, getMaxValue, getMinValueObject + */ Value& getMaxValueObject() { return valueMax; } + /** 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 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 + @param allowNudgingOfOtherValues if false, this value will be restricted to being above the + min value (in a two-value slider) or the mid value (in a three-value + slider). If false, then if this value goes beyond those values, + it will push them along with it. + @see getMaxValue, setMinValue, setValue + */ void setMaxValue (double newValue, bool sendUpdateMessage = true, bool sendMessageSynchronously = false, bool allowNudgingOfOtherValues = false); + /** Adds a listener to be called when this slider's value changes. */ void addListener (SliderListener* listener); + /** Removes a previously-registered listener. */ void removeListener (SliderListener* listener); + /** 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 (bool isDoubleClickEnabled, double valueToSetOnDoubleClick); + /** 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; + /** 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 (bool onlyNotifyOnRelease); + /** 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 (bool shouldSnapToMouse); + /** 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 (bool isEnabled, Component* parentComponentToUse); + /** 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 (bool menuEnabled); + /** This can be used to stop the mouse scroll-wheel from moving the slider. + + By default it's enabled. + */ void setScrollWheelEnabled (bool enabled); + /** Returns a number to indicate which thumb is currently being dragged by the + mouse. + + This will return 0 for the main thumb, 1 for the minimum-value thumb, 2 for + the maximum-value thumb, or -1 if none is currently down. + */ int getThumbBeingDragged() const { return sliderBeingDragged; } + /** 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. @@ -21733,27 +44806,111 @@ public: */ 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); + /** Returns the suffix that was set by setTextValueSuffix(). */ const String getTextValueSuffix() const; + /** 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 (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, 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; + /** True if the slider moves vertically. */ bool isVertical() const; + /** 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. */ @@ -21772,23 +44929,42 @@ public: 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(); + /** @internal */ void valueChanged (Value& value); + /** Returns the best number of decimal places to use when displaying numbers. + This is calculated from the slider's interval setting. + */ int getNumDecimalPlacesToDisplay() const throw() { return numDecimalPlaces; } private: @@ -21852,33 +45028,73 @@ private: 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. */ @@ -21889,15 +45105,39 @@ public: 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, int columnId, int width, @@ -21906,78 +45146,242 @@ public: int propertyFlags = defaultFlags, 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 (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 (bool onlyCountVisibleColumns) const; + /** Returns the name for a column. + @see setColumnName + */ const String getColumnName (int columnId) const; + /** Changes the name of a column. */ void setColumnName (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 (int columnId, int newVisibleIndex); + /** Returns the width of one of the columns. + */ int getColumnWidth (int columnId) const; + /** Changes the width of a column. + + This will cause an asynchronous callback to the tableColumnsResized() method of any registered listeners. + */ void setColumnWidth (int columnId, 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 (int columnId, bool shouldBeVisible); + /** Returns true if this column is currently visible. + @see setColumnVisible + */ bool isColumnVisible (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 (int columnId, 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; + /** Returns true if the table is currently sorted forwards, or false if it's backwards. + @see setSortColumnId + */ bool isSortedForwards() const; + /** 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; + /** 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 (int columnId, bool onlyCountVisibleColumns) const; + /** 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, bool onlyCountVisibleColumns) const; + /** 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 (int index) const; + /** 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 (int xToFind) const; + /** 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 (bool shouldStretchToFit); + /** Returns true if stretch-to-fit has been enabled. + @see setStretchToFitActive + */ bool isStretchToFitActive() const; + /** 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 (bool hasMenu); + /** Returns true if the pop-up menu is enabled. + @see setPopupMenuActive + */ bool isPopupMenuActive() const; + /** 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* newListener); + /** Removes a previously-registered listener. */ void removeListener (TableHeaderListener* listenerToRemove); + /** 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, 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 (int menuReturnId, 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(); + /** Can be overridden for more control over the pop-up menu behaviour. */ virtual void showColumnChooserMenu (int columnIdClicked); juce_UseDebuggingNewOperator @@ -22025,100 +45429,267 @@ private: #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, 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); + /** Returns a tooltip for a particular cell in the table. + */ virtual const String getCellTooltip (int rowNumber, 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* model); + /** Destructor. */ ~TableListBox(); + /** Changes the TableListBoxModel that is being used for this table. + */ void setModel (TableListBoxModel* newModel); + /** Returns the model currently in use. */ TableListBoxModel* getModel() const { return model; } + /** Returns the header component being used in this table. */ TableHeaderComponent* getHeader() const { return header; } + /** Changes the height of the table header component. + @see getHeaderHeight + */ void setHeaderHeight (int newHeight); + /** Returns the height of the table header. + @see setHeaderHeight + */ int getHeaderHeight() const; + /** 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 (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 (bool shouldBeShown); + /** True if the auto-size options should be shown on the menu. + @see setAutoSizeMenuOptionsShown + */ bool isAutoSizeMenuOptionShown() const; + /** 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 (int columnId, int rowNumber, bool relativeToComponentTopLeft) const; + /** Scrolls horizontally if necessary to make sure that a particular column is visible. + + @see ListBox::scrollToEnsureRowIsOnscreen + */ void scrollToEnsureColumnIsOnscreen (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 @@ -22155,14 +45726,29 @@ private: #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 @@ -22173,10 +45759,45 @@ public: 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 (int itemId) = 0; }; @@ -22191,16 +45812,35 @@ public: #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* toolbar); + /** Destructor. */ ~ToolbarItemPalette(); + /** @internal */ void resized(); juce_UseDebuggingNewOperator @@ -22233,19 +45873,78 @@ private: #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; }; @@ -22254,95 +45953,417 @@ public: 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 (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* newItem, 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 (int index, 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 (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 (bool shouldBeSelected, 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 (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(); + /** Sends a repaint message to redraw just this item. + + Note that you should only call this if you want to repaint a superficial change. If + you're altering the tree's nodes, you should instead call treeHasChanged(). + */ void repaintItem() const; + /** 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(); + /** Returns true if all the item's parent nodes are open. + + This is useful to check whether the item might actually be visible or not. + */ bool areAllParentsOpen() const throw(); + /** Changes whether lines are drawn to connect any sub-items to this item. + + By default, line-drawing is turned on. + */ void setLinesDrawnForSubItems (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; } + /** You can override this method to return false if you don't want to allow the + user to select this item. + */ virtual bool canBeSelected() const { return true; } + /** 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); + /** Draws the item's open/close button. + + If you don't implement this method, the default behaviour is to + call LookAndFeel::drawTreeviewPlusMinusBox(), but you can override + it for custom effects. + */ virtual void paintOpenCloseButton (Graphics& g, int width, int height, bool isMouseOver); + /** 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); + /** The item can return a tool tip string here if it wants to. + @see TooltipClient + */ virtual const String getTooltip(); + /** 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. + + To accept drag-and-drop in your tree, see isInterestedInDragSource(), + isInterestedInFileDrag(), etc. + + @see DragAndDropContainer::startDragging + */ virtual const String getDragSourceDescription(); + /** If you want your item to be able to have files drag-and-dropped onto it, implement this + method and return true. + + If you return true and allow some files to be dropped, you'll also need to implement the + filesDropped() method to do something with them. + + Note that this will be called often, so make your implementation very quick! There's + certainly no time to try opening the files and having a think about what's inside them! + + For responding to internal drag-and-drop of other types of object, see isInterestedInDragSource(). + @see FileDragAndDropTarget::isInterestedInFileDrag, isInterestedInDragSource + */ virtual bool isInterestedInFileDrag (const StringArray& files); + /** When files are dropped into this item, this callback is invoked. + + For this to work, you'll need to have also implemented isInterestedInFileDrag(). + The insertIndex value indicates where in the list of sub-items the files were dropped. + @see FileDragAndDropTarget::filesDropped, isInterestedInFileDrag + */ virtual void filesDropped (const StringArray& files, int insertIndex); + /** If you want your item to act as a DragAndDropTarget, implement this method and return true. + + If you implement this method, you'll also need to implement itemDropped() in order to handle + the items when they are dropped. + To respond to drag-and-drop of files from external applications, see isInterestedInFileDrag(). + @see DragAndDropTarget::isInterestedInDragSource, itemDropped + */ virtual bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent); + /** When a things are dropped into this item, this callback is invoked. + + For this to work, you need to have also implemented isInterestedInDragSource(). + The insertIndex value indicates where in the list of sub-items the new items should be placed. + @see isInterestedInDragSource, DragAndDropTarget::itemDropped + */ virtual void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex); + /** Sets a flag to indicate that the item wants to be allowed + to draw all the way across to the left edge of the treeview. + + By default this is false, which means that when the paintItem() + method is called, its graphics context is clipped to only allow + drawing within the item's rectangle. If this flag is set to true, + then the graphics context isn't clipped on its left side, so it + can draw all the way across to the left margin. Note that the + context will still have its origin in the same place though, so + the coordinates of anything to its left will be negative. It's + mostly useful if you want to draw a wider bar behind the + highlighted item. + */ void setDrawsInLeftMargin (bool canDrawInLeftMargin) throw(); + /** Saves the current state of open/closed nodes so it can be restored later. + + This takes a snapshot of which sub-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. + + You'd normally want to use TreeView::getOpennessState() rather than call it + for a specific item, but this can be handy if you need to briefly save the state + for a section of the tree. + + The caller is responsible for deleting the object that is returned. + @see TreeView::getOpennessState, restoreOpennessState + */ XmlElement* getOpennessState() const throw(); + /** Restores the openness of this item and all its sub-items from a saved state. + + See TreeView::restoreOpennessState for more details. + + You'd normally want to use TreeView::restoreOpennessState() rather than call it + for a specific item, but this can be handy if you need to briefly save the state + for a section of the tree. + + @see TreeView::restoreOpennessState, getOpennessState + */ void restoreOpennessState (const XmlElement& xml) throw(); + /** Returns the index of this item in its parent's sub-items. */ int getIndexInParent() const throw(); + /** Returns true if this item is the last of its parent's sub-itens. */ bool isLastOfSiblings() const throw(); + /** Creates a string that can be used to uniquely retrieve this item in the tree. + + The string that is returned can be passed to TreeView::findItemFromIdentifierString(). + The string takes the form of a path, constructed from the getUniqueName() of this + item and all its parents, so these must all be correctly implemented for it to work. + @see TreeView::findItemFromIdentifierString, getUniqueName + */ const String getItemIdentifierString() const; juce_UseDebuggingNewOperator @@ -22381,6 +46402,12 @@ private: 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, public FileDragAndDropTarget, @@ -22389,58 +46416,198 @@ class JUCE_API TreeView : public Component, { 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. You can also use deleteRootItem() as a quick way + to delete it. + */ void setRootItem (TreeViewItem* 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; } + /** This will remove and delete the current root item. + + It's a convenient way of deleting the item and calling setRootItem (0). + */ void deleteRootItem(); + /** 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 (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 (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 (bool canMultiSelect); + /** Returns whether multi-select has been enabled for the tree. + + @see setMultiSelectEnabled + */ bool isMultiSelectEnabled() const throw() { return multiSelectEnabled; } + /** Sets a flag to indicate whether to hide the open/close buttons. + + @see areOpenCloseButtonsVisible + */ void setOpenCloseButtonsVisible (bool shouldBeVisible); + /** Returns whether open/close buttons are shown. + + @see setOpenCloseButtonsVisible + */ bool areOpenCloseButtonsVisible() const throw() { return openCloseButtonsVisible; } + /** 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 (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; + /** Returns the item that contains a given y position. + The y is relative to the top of the TreeView component. + */ TreeViewItem* getItemAt (int yPosition) const throw(); + /** 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(); + /** 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 (int newIndentSize); + /** Searches the tree for an item with the specified identifier. + The identifer string must have been created by calling TreeViewItem::getItemIdentifierString(). + If no such item exists, this will return false. If the item is found, all of its items + will be automatically opened. + */ TreeViewItem* findItemFromIdentifierString (const String& identifierString) const; + /** 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 (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. */ @@ -22448,20 +46615,35 @@ public: dragAndDropIndicatorColourId = 0x1000502 /**< The colour to use for the drag-and-drop target position indicator. */ }; + /** @internal */ void paint (Graphics& g); + /** @internal */ void resized(); + /** @internal */ bool keyPressed (const KeyPress& key); + /** @internal */ void colourChanged(); + /** @internal */ void enablementChanged(); + /** @internal */ bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ void fileDragEnter (const StringArray& files, int x, int y); + /** @internal */ void fileDragMove (const StringArray& files, int x, int y); + /** @internal */ void fileDragExit (const StringArray& files); + /** @internal */ void filesDropped (const StringArray& files, int x, int y); + /** @internal */ bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent); + /** @internal */ void itemDragEnter (const String& sourceDescription, Component* sourceComponent, int x, int y); + /** @internal */ void itemDragMove (const String& sourceDescription, Component* sourceComponent, int x, int y); + /** @internal */ void itemDragExit (const String& sourceDescription, Component* sourceComponent); + /** @internal */ void itemDropped (const String& sourceDescription, Component* sourceComponent, int x, int y); juce_UseDebuggingNewOperator @@ -22521,18 +46703,38 @@ private: #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: @@ -22548,6 +46750,27 @@ protected: #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: @@ -22559,53 +46782,157 @@ public: SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ }; + /** Creates an in-memory image with a specified size and format. + + To create an image that can use native OS rendering methods, see createNativeImage(). + + @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 (PixelFormat format, int imageWidth, int imageHeight, bool clearImage); + /** Creates a copy of another image. + + @see createCopy + */ Image (const Image& other); + /** Destructor. */ virtual ~Image(); + /** Tries to create an image that is uses native drawing methods when you render + onto it. + + On some platforms this will just return a normal software-based image. + */ static Image* createNativeImage (PixelFormat format, int imageWidth, int imageHeight, bool clearImage); + /** 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 a rectangle with the same size as this image. + The rectangle is always at position (0, 0). + */ const Rectangle getBounds() const throw() { return Rectangle (0, 0, imageWidth, 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, Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; + /** Returns a new single-channel image which is a copy of the alpha-channel of this image. + */ virtual Image* createCopyOfAlphaChannel() 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 (int x, 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 (int x, 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 (int x, int y, 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 (float amountToMultiplyBy); + /** Changes all the colours to be shades of grey, based on their current luminosity. + */ virtual void desaturate(); + /** Retrieves a section of an image as raw pixel data, so it can be read or written to. + + You should only use this class as a last resort - messing about with the internals of + an image is only recommended for people who really know what they're doing! + + A BitmapData object should be used as a temporary, stack-based object. Don't keep one + hanging around while the image is being used elsewhere. + + Depending on the way the image class is implemented, this may create a temporary buffer + which is copied back to the image when the object is deleted, or it may just get a pointer + directly into the image's raw data. + + You can use the stride and data values in this class directly, but don't alter them! + The actual format of the pixel data depends on the image's format - see Image::getFormat(), + and the PixelRGB, PixelARGB and PixelAlpha classes for more info. + */ class BitmapData { public: @@ -22613,8 +46940,16 @@ public: BitmapData (const Image& image, int x, int y, int w, int h); ~BitmapData(); + /** Returns a pointer to the start of a line in the image. + The co-ordinate you provide here isn't checked, so it's the caller's responsibility to make + sure it's not out-of-range. + */ inline uint8* getLinePointer (int y) const { return data + y * lineStride; } + /** Returns a pointer to a pixel in the image. + The co-ordinates you give here are not checked, so it's the caller's responsibility to make sure they're + not out-of-range. + */ inline uint8* getPixelPointer (int x, int y) const { return data + y * lineStride + x * pixelStride; } uint8* data; @@ -22625,18 +46960,36 @@ public: BitmapData& operator= (const BitmapData&); }; + /** 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, 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: @@ -22644,6 +46997,7 @@ protected: const PixelFormat format; const int imageWidth, imageHeight; + /** Used internally so that subclasses can call a constructor that doesn't allocate memory */ Image (PixelFormat format, int imageWidth, int imageHeight); @@ -22660,58 +47014,156 @@ private: #endif // __JUCE_IMAGE_JUCEHEADER__ /*** End of inlined file: juce_Image.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* 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, bool includeDirectories, bool includeFiles); + /** Returns the directory that's currently being used. */ const File& getDirectory() const; + /** 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 (bool shouldIgnoreHiddenFiles); + /** Returns true if hidden files are ignored. + @see setIgnoresHiddenFiles + */ bool ignoresHiddenFiles() const; + /** 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 (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 (int index) const; + /** Returns the file filter being used. + + The filter is specified in the constructor. + */ const FileFilter* getFilter() const { return fileFilter; } + /** @internal */ bool useTimeSlice(); + /** @internal */ TimeSliceThread& getTimeSliceThread() { return thread; } + /** @internal */ static int compareElements (const DirectoryContentsList::FileInfo* first, const DirectoryContentsList::FileInfo* second); @@ -22748,50 +47200,91 @@ private: #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: + /** Creates a DirectoryContentsDisplayComponent for a given list of files. */ DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow); + /** Destructor. */ virtual ~DirectoryContentsDisplayComponent(); + /** Returns the number of files the user has got selected. + @see getSelectedFile + */ virtual int getNumSelectedFiles() const = 0; + /** Returns one of the files that the user has currently selected. + The index should be in the range 0 to (getNumSelectedFiles() - 1). + @see getNumSelectedFiles + */ virtual const File getSelectedFile (int index) const = 0; + /** Deselects any selected files. */ virtual void deselectAllFiles() = 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* listener); + /** Removes a listener. + @see addListener + */ void removeListener (FileBrowserListener* listener); + /** A set of colour IDs to use to change the colour of various aspects of the list. + + 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 { 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 @@ -22823,14 +47316,32 @@ protected: #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 @@ -22843,6 +47354,15 @@ private: #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, @@ -22853,6 +47373,10 @@ class JUCE_API FileBrowserComponent : public Component, { public: + /** Various options for the browser. + + A combination of these is passed into the FileBrowserComponent constructor. + */ enum FileChooserFlags { openMode = 1, /**< specifies that the component should allow the user to @@ -22868,53 +47392,128 @@ public: filenameBoxIsReadOnly = 64 /**< specifies that the user can't type directly into the filename box. */ }; + /** Creates a FileBrowserComponent. + + @param flags A combination of flags from the FileChooserFlags enumeration, + used to specify the component's behaviour. The flags must contain + either openMode or saveMode, and canSelectFiles and/or + canSelectDirectories. + @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 + */ FileBrowserComponent (int flags, const File& initialFileOrDirectory, const FileFilter* fileFilter, FilePreviewComponent* previewComp); + /** Destructor. */ ~FileBrowserComponent(); + /** Returns the number of files that the user has got selected. + If multiple select isn't active, this will only be 0 or 1. To get the complete + list of files they've chosen, pass an index to getCurrentFile(). + */ int getNumSelectedFiles() const throw(); + /** Returns one of the files that the user has chosen. + If the box has multi-select enabled, the index lets you specify which of the files + to get - see getNumSelectedFiles() to find out how many files were chosen. + @see getHighlightedFile + */ const File getSelectedFile (int index) const throw(); + /** Deselects any files that are currently selected. + */ void deselectAllFiles(); + /** Returns true if the currently selected file(s) are 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, this only returns true if a file has been selected and if it exists. + In a "save" mode, a non-existent file would also be valid. + */ bool currentFileIsValid() const; + /** This returns the last item in the view that the user has highlighted. + This may be different from getCurrentFile(), which returns the value + that is shown in the filename box, and if there are multiple selections, + this will only return one of them. + @see getSelectedFile + */ const File getHighlightedFile() const throw(); + /** 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 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; + /** Returns true if the saveMode flag was set when this component was created. + */ bool isSaveMode() const throw(); + /** Adds a listener to be told when the user selects and clicks on files. + + @see removeListener + */ void addListener (FileBrowserListener* listener); + /** Removes a listener. + + @see addListener + */ void removeListener (FileBrowserListener* listener); + /** @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 */ bool keyPressed (const KeyPress& key); + /** @internal */ void selectionChanged(); + /** @internal */ void fileClicked (const File& f, const MouseEvent& e); + /** @internal */ void fileDoubleClicked (const File& f); + /** @internal */ bool isFileSuitable (const File& file) const; + /** @internal */ bool isDirectorySuitable (const File&) const; + /** @internal */ FilePreviewComponent* getPreviewComponent() const throw(); juce_UseDebuggingNewOperator @@ -22961,29 +47560,141 @@ private: #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, 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 (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(); + /** Same as browseForFileToOpen, but allows the user to select multiple files and directories. + + The files that are returned can be obtained by calling getResults(). See + browseForFileToOpen() for more info about the behaviour of this method. + */ bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = 0); + /** 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 Array& getResults() const; juce_UseDebuggingNewOperator @@ -23030,23 +47741,50 @@ private: #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 (float alpha = 0.5f, int xOffset = 1, int yOffset = 5, 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 @@ -23077,43 +47815,121 @@ private: #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, 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, int width, int height); + /** Turns the drop-shadow on and off. */ void setDropShadowEnabled (bool useShadow); + /** Sets whether an OS-native title bar will be used, or a Juce one. + + @see isUsingNativeTitleBar + */ void setUsingNativeTitleBar (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 (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: @@ -23140,50 +47956,112 @@ private: #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 (int minimumWidth) throw(); + /** Returns the current minimum width. */ int getMinimumWidth() const throw() { return minW; } + /** Imposes a maximum width limit. */ void setMaximumWidth (int maximumWidth) throw(); + /** Returns the current maximum width. */ int getMaximumWidth() const throw() { return maxW; } + /** Imposes a minimum height limit. */ void setMinimumHeight (int minimumHeight) throw(); + /** Returns the current minimum height. */ int getMinimumHeight() const throw() { return minH; } + /** Imposes a maximum height limit. */ void setMaximumHeight (int maximumHeight) throw(); + /** Returns the current maximum height. */ int getMaximumHeight() const throw() { return maxH; } + /** Imposes a minimum width and height limit. */ void setMinimumSize (int minimumWidth, int minimumHeight) throw(); + /** Imposes a maximum width and height limit. */ void setMaximumSize (int maximumWidth, int maximumHeight) throw(); + /** Set all the maximum and minimum dimensions. */ void setSizeLimits (int minimumWidth, int minimumHeight, int maximumWidth, 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 (int minimumWhenOffTheTop, int minimumWhenOffTheLeft, int minimumWhenOffTheBottom, 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 (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 bounds the target position 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 (Rectangle& bounds, const Rectangle& previousBounds, const Rectangle& limits, @@ -23192,10 +48070,13 @@ public: bool isStretchingBottom, 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, const Rectangle& bounds, bool isStretchingTop, @@ -23203,8 +48084,17 @@ public: bool isStretchingBottom, bool isStretchingRight); + /** Performs a check on the current size of a component, and moves or resizes + it if it fails the constraints. + */ void checkComponentBounds (Component* component); + /** 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, const Rectangle& bounds); @@ -23222,17 +48112,63 @@ private: #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); @@ -23254,19 +48190,63 @@ private: #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* componentToResize, ComponentBoundsConstrainer* constrainer); + /** Destructor. */ ~ResizableBorderComponent(); + /** Specifies how many pixels wide the draggable edges of this component are. + + @see getBorderThickness + */ void setBorderThickness (const BorderSize& newBorderSize); + /** Returns the number of pixels wide that the draggable edges of this component are. + + @see setBorderThickness + */ const BorderSize getBorderThickness() const; + /** Represents the different sections of a resizable border, which allow it to + resized in different ways. + */ class Zone { public: @@ -23280,6 +48260,7 @@ public: bottom = 8 }; + /** Creates a Zone from a combination of the flags in \enum Zones. */ explicit Zone (int zoneFlags = 0) throw(); Zone (const Zone& other) throw(); Zone& operator= (const Zone& other) throw(); @@ -23287,24 +48268,40 @@ public: bool operator== (const Zone& other) const throw(); bool operator!= (const Zone& other) const throw(); + /** Given a point within a rectangle with a resizable border, this returns the + zone that the point lies within. + */ static const Zone fromPositionOnBorder (const Rectangle& totalSize, const BorderSize& border, const Point& position); + /** Returns an appropriate mouse-cursor for this resize zone. */ const MouseCursor getMouseCursor() const throw(); + /** Returns true if dragging this zone will move the enire object without resizing it. */ bool isDraggingWholeObject() const throw() { return zone == centre; } + /** Returns true if dragging this zone will move the object's left edge. */ bool isDraggingLeftEdge() const throw() { return (zone & left) != 0; } + /** Returns true if dragging this zone will move the object's right edge. */ bool isDraggingRightEdge() const throw() { return (zone & right) != 0; } + /** Returns true if dragging this zone will move the object's top edge. */ bool isDraggingTopEdge() const throw() { return (zone & top) != 0; } + /** Returns true if dragging this zone will move the object's bottom edge. */ bool isDraggingBottomEdge() const throw() { return (zone & bottom) != 0; } + /** Resizes this rectangle by the given amount, moving just the edges that this zone + applies to. + */ const Rectangle resizeRectangleBy (Rectangle original, const Point& distance) const throw(); + /** Resizes this rectangle by the given amount, moving just the edges that this zone + applies to. + */ const Rectangle resizeRectangleBy (Rectangle original, const Point& distance) const throw(); + /** Returns the raw flags for this zone. */ int getZoneFlags() const throw() { return zone; } private: @@ -23315,12 +48312,19 @@ public: 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: @@ -23344,22 +48348,55 @@ private: #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* componentToResize, ComponentBoundsConstrainer* 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: @@ -23375,59 +48412,241 @@ private: #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. + + This constructor doesn't specify a background colour, so the LookAndFeel's default + background colour will be used. + + @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 + */ ResizableWindow (const String& name, bool addToDesktop); + /** 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, 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. + + This method is the same as retrieving the colour for ResizableWindow::backgroundColourId. + + @see setBackgroundColour + */ const Colour getBackgroundColour() const throw(); + /** 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). + + You can also use the ResizableWindow::backgroundColourId colour id to set + this colour. + + @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 (bool shouldBeResizable, 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 (int newMinimumWidth, int newMinimumHeight, int newMaximumWidth, 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 (const Rectangle& bounds); + /** 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 (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 (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* newContentComponent, bool deleteOldOne = true, 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); + /** A set of colour IDs to use to change the colour of various aspects of the window. + + 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 = 0x1005700, /**< A colour to use to fill the window's background. */ @@ -23436,24 +48655,55 @@ public: 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(); #if 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* 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* child, int zOrder = -1); #endif @@ -23492,28 +48742,50 @@ private: #ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ #define __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ +/** + A 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 { return character; } + /** Checks whether the glyph is actually empty. */ bool isWhitespace() const { return CharacterFunctions::isWhitespace (character); } + /** Returns the position of the glyph's left-hand edge. */ float getLeft() const { return x; } + /** Returns the position of the glyph's right-hand edge. */ float getRight() const { return x + w; } + /** Returns the y position of the glyph's baseline. */ float getBaselineY() const { return y; } + /** Returns the y position of the top of the glyph. */ float getTop() const { return y - font.getAscent(); } + /** Returns the y position of the bottom of the glyph. */ float getBottom() const { return y + font.getDescent(); } + /** Returns the bounds of the glyph. */ const Rectangle getBounds() const { return Rectangle (x, getTop(), w, font.getHeight()); } + /** Shifts the glyph's position by a relative amount. */ void moveBy (float deltaX, float deltaY); + /** Draws the glyph into a graphics context. */ void draw (const Graphics& g) const; + /** Draws the glyph into a graphics context, with an extra transform applied to it. */ void draw (const Graphics& g, const AffineTransform& transform) const; + /** Returns the path for this glyph. + + @param path the glyph's outline will be appended to this path + */ void createPath (Path& path) const; + /** Checks to see if a point lies within this glyph. */ bool hitTest (float x, float y) const; juce_UseDebuggingNewOperator @@ -23530,40 +48802,107 @@ private: PositionedGlyph (const PositionedGlyph& other); }; +/** + 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(); + /** Takes a copy of another arrangement. */ GlyphArrangement (const GlyphArrangement& other); + /** Copies another arrangement onto this one. + + To add another arrangement without clearing this one, use addGlyphArrangement(). + */ GlyphArrangement& operator= (const GlyphArrangement& other); + /** Destructor. */ ~GlyphArrangement(); + /** Returns the total number of glyphs in the arrangement. */ int getNumGlyphs() const throw() { return glyphs.size(); } + /** 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 (int index) const; + /** Clears all text from the arrangement and resets it. + */ void clear(); + /** 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, float x, float y); + /** 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, float y, float maxWidthPixels, bool useEllipsis); + /** 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, float maxLineWidth, const Justification& horizontalLayout); + /** 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, @@ -23571,26 +48910,82 @@ public: int maximumLinesToUse, float minimumHorizontalScale = 0.7f); + /** Appends another glyph arrangement to this one. */ void addGlyphArrangement (const GlyphArrangement& other); + /** 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; + /** 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; + /** Converts the set of glyphs into a path. + + @param path the glyphs' outlines will be appended to this path + */ void createPath (Path& path) const; + /** 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; + /** 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 includeWhitespace if true, the extent of any whitespace characters will also + be taken into account + */ const Rectangle getBoundingBox (int startIndex, int numGlyphs, bool includeWhitespace) const; + /** 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, float deltaX, float deltaY); + /** 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); + /** 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, float horizontalScaleFactor); + /** 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 (int startIndex, int numGlyphs, float x, float y, float width, float height, const Justification& justification); @@ -23609,31 +49004,98 @@ private: #endif // __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ /*** End of inlined file: juce_GlyphArrangement.h ***/ +/** + 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 ("*.foo", "Foo files"); + + FileBrowserComponent browser (FileBrowserComponent::loadFileMode, + File::nonexistent, + &wildcardFilter, + 0); + + FileChooserDialogBox dialogBox ("Open some kind of file", + "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, 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); + /** A set of colour IDs to use to change the colour of various aspects of the 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 { titleTextColourId = 0x1000850, /**< The colour to use to draw the box's title. */ }; + /** @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 @@ -23678,6 +49140,17 @@ private: #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 ListBox, public DirectoryContentsDisplayComponent, private ListBoxModel, @@ -23685,24 +49158,43 @@ class JUCE_API FileListComponent : public ListBox, { public: + /** Creates a listbox to show the contents of a specified directory. + */ FileListComponent (DirectoryContentsList& listToShow); + /** Destructor. */ ~FileListComponent(); + /** Returns the number of files the user has got selected. + @see getSelectedFile + */ int getNumSelectedFiles() const; + /** Returns one of the files that the user has currently selected. + The index should be in the range 0 to (getNumSelectedFiles() - 1). + @see getNumSelectedFiles + */ const File getSelectedFile (int index = 0) const; + /** Deselects any files that are currently selected. */ void deselectAllFiles(); + /** 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 @@ -23727,14 +49219,37 @@ private: 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, @@ -23744,6 +49259,24 @@ class JUCE_API FilenameComponent : public Component, { 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, bool canEditFilename, @@ -23753,40 +49286,95 @@ public: 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, bool addToRecentlyUsedList, bool sendChangeNotification = true); + /** Changes whether the use can type into the filename box. + */ void setFilenameIsEditable (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); + /** 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 (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* listener); + /** Removes a previously-registered listener. */ void removeListener (FilenameComponentListener* listener); + /** 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 @@ -23824,6 +49412,12 @@ private: #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, @@ -23832,32 +49426,60 @@ class JUCE_API FileSearchPathListComponent : public Component, { 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); + /** 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 @@ -23892,25 +49514,55 @@ private: #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 TreeView, public DirectoryContentsDisplayComponent { public: + /** Creates a listbox to show the contents of a specified directory. + */ FileTreeComponent (DirectoryContentsList& listToShow); + /** Destructor. */ ~FileTreeComponent(); + /** Returns the number of files the user has got selected. + @see getSelectedFile + */ int getNumSelectedFiles() const { return TreeView::getNumSelectedItems(); } + /** Returns one of the files that the user has currently selected. + The index should be in the range 0 to (getNumSelectedFiles() - 1). + @see getNumSelectedFiles + */ const File getSelectedFile (int index = 0) const; + /** Deselects any files that are currently selected. */ void deselectAllFiles(); + /** 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); + /** Returns the last value that was set by setDragAndDropDescription(). + */ const String& getDragAndDropDescription() const throw() { return dragAndDropDescription; } juce_UseDebuggingNewOperator @@ -23933,17 +49585,27 @@ private: #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 @@ -23970,18 +49632,39 @@ private: #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& fileWildcardPatterns, const String& directoryWildcardPatterns, 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 @@ -24024,48 +49707,190 @@ private: #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 + */ explicit KeyPressMappingSet (ApplicationCommandManager* commandManager); + /** Creates an copy of a KeyPressMappingSet. */ KeyPressMappingSet (const KeyPressMappingSet& other); + /** 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 (CommandID commandID) const; + /** 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 (CommandID commandID, const KeyPress& newKeyPress, int insertIndex = -1); + /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager. + + @see resetToDefaultMapping + */ void resetToDefaultMappings(); + /** Resets all key-mappings to the defaults for a particular command. + + @see resetToDefaultMappings + */ void resetToDefaultMapping (CommandID commandID); + /** Removes all keypresses that are assigned to any commands. */ void clearAllKeyPresses(); + /** Removes all keypresses that are assigned to a particular command. */ void clearAllKeyPresses (CommandID commandID); + /** 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 (CommandID commandID, int keyPressIndex); + /** Removes a keypress from any command that it may be assigned to. + */ void removeKeyPress (const KeyPress& keypress); + /** Returns true if the given command is linked to this key. */ bool containsMapping (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 (bool saveDifferencesFromDefaultSet) const; + /** @internal */ bool keyPressed (const KeyPress& key, Component* originatingComponent); + /** @internal */ bool keyStateChanged (bool isKeyDown, Component* originatingComponent); + /** @internal */ void globalFocusChanged (Component* focusedComponent); juce_UseDebuggingNewOperator @@ -24105,6 +49930,12 @@ private: #endif // __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ /*** End of inlined file: juce_KeyPressMappingSet.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, @@ -24112,33 +49943,83 @@ class JUCE_API KeyMappingEditorComponent : public Component, { 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* mappingSet, 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 (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 (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); + /** 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. + + To change the colours of the menu that pops up + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ enum ColourIds { backgroundColourId = 0x100ad00, /**< The background colour to fill the editor background. */ textColourId = 0x100ad01, /**< The colour for the text. */ }; + /** @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 @@ -24188,21 +50069,44 @@ private: #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* 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: @@ -24231,31 +50135,64 @@ private: #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); + /** Returns the currently displayed text label. */ const String getText() const; + /** 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: @@ -24289,16 +50226,32 @@ private: 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* ownerBar, 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 (int depth); void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); @@ -24313,6 +50266,11 @@ protected: 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: @@ -24320,12 +50278,29 @@ private: 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, @@ -24334,48 +50309,126 @@ public: TabsAtRight }; + /** Creates a TabbedButtonBar with a given placement. + + You can change the orientation later if you need to. + */ TabbedButtonBar (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 (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 (int tabIndex, const String& newName); + /** Gets rid of one of the tabs. */ void removeTab (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 (int currentIndex, 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, bool sendChangeMessage = true); + /** 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; } + /** Returns the button for a specific tab. + + The button that is returned may be deleted later by this component, so don't hang + on to the pointer that is returned. A null pointer may be returned if the index is + out of range. + */ TabBarButton* getTabButton (int index) const; + /** Callback method to indicate the selected tab has been changed. + + @see setCurrentTabIndex + */ virtual void currentTabChanged (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 (int tabIndex, const String& tabName); + /** Returns the colour of a tab. + + This is the colour that was specified in addTab(). + */ const Colour getTabBackgroundColour (int tabIndex); + /** Changes the background colour of a tab. + + @see addTab, getTabBackgroundColour + */ void setTabBackgroundColour (int tabIndex, const Colour& newColour); + /** 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 { tabOutlineColourId = 0x1005812, /**< The colour to use to draw an outline around the tabs. */ @@ -24387,14 +50440,22 @@ public: colour. */ }; + /** @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, int tabIndex); private: @@ -24413,64 +50474,168 @@ private: #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. + */ explicit TabbedComponent (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 (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 (int newDepth); + /** Returns the current thickness of the tab bar. + + @see setTabBarDepth + */ int getTabBarDepth() const throw() { return tabDepth; } + /** Specifies the thickness of an outline that should be drawn around the content component. + + If this thickness is > 0, a line 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. + + To set the colour of the line, use setColour (outlineColourId, ...). + */ void setOutline (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 (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* contentComponent, bool deleteComponentWhenNotNeeded, int insertIndex = -1); + /** Changes the name of one of the tabs. */ void setTabName (int tabIndex, const String& newName); + /** Gets rid of one of the tabs. */ void removeTab (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 (int tabIndex) const throw(); + /** Returns the colour of one of the tabs. */ const Colour getTabBackgroundColour (int tabIndex) const throw(); + /** Changes the background colour of one of the tabs. */ void setTabBackgroundColour (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 (int newTabIndex, bool sendChangeMessage = true); + /** 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 (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 (int tabIndex, const String& tabName); + /** Returns the tab button bar component that is being used. + */ TabbedButtonBar& getTabbedButtonBar() const throw() { return *tabs; } + /** 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 = 0x1005800, /**< The colour to fill the background behind the tabs. */ @@ -24478,8 +50643,11 @@ public: (See setOutline) */ }; + /** @internal */ void paint (Graphics& g); + /** @internal */ void resized(); + /** @internal */ void lookAndFeelChanged(); juce_UseDebuggingNewOperator @@ -24488,6 +50656,11 @@ 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, int tabIndex); private: @@ -24524,17 +50697,36 @@ private: 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 { @@ -24542,34 +50734,97 @@ 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* 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* listenerToAdd) throw(); + /** Removes a listener. + + @see addListener + */ void removeListener (MenuBarModelListener* 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. + + An optional extra menu can be specified, containing items to add to the top of + the apple menu. (Confusingly, the 'apple' menu isn't the one with a picture of + an apple, it's the one next to it, with your application's name at the top + and the services menu etc on it). When one of these items is selected, the + menu bar model will be used to invoke it, and in the menuItemSelected() callback + the topLevelMenuIndex parameter will be -1. If you pass in an extraAppleMenuItems + object then newMenuBarModel must be non-null. + */ static void setMacMainMenu (MenuBarModel* newMenuBarModel, const PopupMenu* extraAppleMenuItems = 0); + /** MAC ONLY - Returns the menu model that is currently being shown as + the main menu bar. + */ static MenuBarModel* getMacMainMenu(); #endif + /** @internal */ void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info); + /** @internal */ void applicationCommandListChanged(); + /** @internal */ void handleAsyncUpdate(); juce_UseDebuggingNewOperator @@ -24585,32 +50840,67 @@ private: #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* 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* 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 (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); @@ -24639,70 +50929,208 @@ private: #endif // __JUCE_MENUBARCOMPONENT_JUCEHEADER__ /*** End of inlined file: juce_MenuBarComponent.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, int requiredButtons, 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 (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 (int requiredButtons, 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 (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, 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(); + /** A set of colour IDs to use to change the colour of various aspects of the window. + + 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 = 0x1005701, /**< The colour to draw any text with. It's up to the look and feel class how this is used. */ }; + /** @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(); + /** @internal */ const Rectangle getTitleBarArea(); juce_UseDebuggingNewOperator @@ -24731,17 +51159,33 @@ private: 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 @@ -24751,60 +51195,209 @@ private: 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 (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* component, const Colour& backgroundColour, 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, 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 (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 (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 (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 (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 @@ -24845,38 +51438,203 @@ private: #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 (int itemIndex, double minimumSize, double maximumSize, 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 (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** components, int numComponents, int x, int y, int width, int height, bool vertically, 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 (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 (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 (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 (int itemIndex, int newPosition); @@ -24916,20 +51674,55 @@ private: #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* layoutToUse, int itemIndexInLayout, 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 @@ -24954,23 +51747,57 @@ private: #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 (double currentSize, double minSize, double maxSize, 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 (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 (int index) const throw(); juce_UseDebuggingNewOperator @@ -25022,42 +51849,100 @@ private: 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(); + /** Creates a copy of another layout object. */ TextLayout (const TextLayout& other); + /** Creates a text layout from an initial string and font. */ TextLayout (const String& text, const Font& font); + /** Destructor. */ ~TextLayout(); + /** Copies another layout onto this one. */ TextLayout& operator= (const TextLayout& layoutToCopy); + /** Clears the layout, removing all its text. */ void clear(); + /** 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); + /** 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); + /** 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, bool attemptToBalanceLineLengths); + /** Returns the overall width of the entire text layout. */ int getWidth() const; + /** Returns the overall height of the entire text layout. */ int getHeight() const; + /** Returns the total number of lines of text. */ int getNumLines() const { return totalLines; } + /** Returns the width of a particular line of text. + + @param lineNumber the line, from 0 to (getNumLines() - 1) + */ int getLineWidth (int lineNumber) const; + /** Renders the text at a specified position using a graphics context. + */ void draw (Graphics& g, int topLeftX, int topLeftY) const; + /** 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; @@ -25074,11 +51959,24 @@ private: #endif // __JUCE_TEXTLAYOUT_JUCEHEADER__ /*** End of inlined file: juce_TextLayout.h ***/ +/** 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. */ @@ -25091,59 +51989,194 @@ public: 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 + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + */ AlertWindow (const String& title, const String& message, AlertIconType iconType, Component* associatedComponent = 0); + /** 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, 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, 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* 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 (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 (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. + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + */ static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, const String& title, const String& message, const String& buttonText = String::empty, Component* associatedComponent = 0); + /** 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. + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @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, @@ -25151,6 +52184,31 @@ public: const String& button2Text = String::empty, Component* associatedComponent = 0); + /** 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) + @param associatedComponent if this is non-zero, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + + @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, @@ -25159,10 +52217,25 @@ public: const String& button3Text = String::empty, Component* associatedComponent = 0); + /** 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. */ @@ -25173,13 +52246,21 @@ public: 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(); + /** @internal */ void userTriedToCloseWindow(); + /** @internal */ int getDesktopWindowStyleFlags() const; private: @@ -25231,30 +52312,86 @@ class DirectoryContentsDisplayComponent; class FilePreviewComponent; class ImageButton; +/** + 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 (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 (int colourId, const Colour& colour) throw(); + /** Returns true if the specified colour ID has been explicitly set using the + setColour() method. + */ bool isColourSpecified (int colourId) const throw(); virtual const Typeface::Ptr getTypefaceForFont (const Font& font); + /** Allows you to change the default sans-serif font. + + If you need to supply your own Typeface object for any of the default fonts, rather + than just supplying the name (e.g. if you want to use an embedded font), then + you should instead override getTypefaceForFont() to create and return the typeface. + */ void setDefaultSansSerifTypefaceName (const String& newName); + /** Override this to get the chance to swap a component's mouse cursor for a + customised one. + */ virtual const MouseCursor getMouseCursorFor (Component& component); + /** Draws the lozenge-shaped background for a standard button. */ virtual void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour, @@ -25263,11 +52400,13 @@ public: virtual const Font getFontForTextButton (TextButton& button); + /** 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, @@ -25283,6 +52422,8 @@ public: bool isMouseOverButton, bool isButtonDown); + /* AlertWindow handling.. + */ virtual AlertWindow* createAlertWindow (const String& title, const String& message, const String& button1, @@ -25303,6 +52444,14 @@ public: virtual const Font getAlertWindowFont(); + /** 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); @@ -25313,6 +52462,17 @@ public: virtual void drawSpinningWaitAnimation (Graphics& g, const Colour& colour, int x, int y, int w, int h); + /** 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, @@ -25321,6 +52481,23 @@ public: 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, @@ -25331,17 +52508,24 @@ public: 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 (float height); + /** Returns a cross shape for use in yes/no boxes, etc. */ virtual const Path getCrossShape (float height); + /** Draws the + or - box in a treeview. */ virtual void drawTreeviewPlusMinusBox (Graphics& g, int x, int y, int w, int h, bool isPlus, bool isMouseOver); virtual void fillTextEditorBackground (Graphics& g, int width, int height, TextEditor& textEditor); @@ -25377,8 +52561,10 @@ public: 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, bool isSeparator, @@ -25391,12 +52577,14 @@ public: 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, bool isSeparator, int standardMenuItemHeight, @@ -25633,8 +52821,11 @@ public: virtual void drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription); + /** + */ virtual void playAlertSound(); + /** Utility function to draw a shiny, glassy circle (for round LED-type buttons). */ static void drawGlassSphere (Graphics& g, float x, float y, float diameter, @@ -25647,6 +52838,7 @@ public: const Colour& colour, float outlineThickness, int direction) throw(); + /** Utility function to draw a shiny, glassy oblong (for text buttons). */ static void drawGlassLozenge (Graphics& g, float x, float y, float width, float height, @@ -25692,20 +52884,28 @@ private: #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, @@ -25747,6 +52947,7 @@ public: 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, @@ -25820,20 +53021,48 @@ private: #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 ReferenceCountedObject { 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 (bool isTriggeredAutomatically = true); private: @@ -25874,6 +53103,20 @@ private: #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 { @@ -25881,20 +53124,24 @@ public: typedef SelectableItemType ItemType; + /** Creates an empty set. */ SelectedItemSet() { } + /** Creates a set based on an array of items. */ explicit 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. */ SelectedItemSet& operator= (const SelectedItemSet& other) { if (selectedItems != other.selectedItems) @@ -25906,10 +53153,18 @@ public: 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)) @@ -25933,6 +53188,12 @@ public: } } + /** 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)) @@ -25944,6 +53205,27 @@ public: } } + /** 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) { @@ -25964,6 +53246,23 @@ public: } } + /** 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) { @@ -25978,6 +53277,20 @@ public: } } + /** 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, @@ -25987,6 +53300,7 @@ public: addToSelectionBasedOnModifiers (item, modifiers); } + /** Deselects an item. */ void deselect (SelectableItemType item) { const int i = selectedItems.indexOf (item); @@ -25998,6 +53312,7 @@ public: } } + /** Deselects all items. */ void deselectAll() { if (selectedItems.size() > 0) @@ -26012,16 +53327,27 @@ public: } } + /** 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); @@ -26029,10 +53355,22 @@ public: 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) @@ -26050,33 +53388,100 @@ private: #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, const Rectangle& area) = 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. + */ explicit 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) { @@ -26093,6 +53498,18 @@ public: dragStartPos = e.getMouseDownPosition(); } + /** 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) @@ -26121,6 +53538,10 @@ public: } } + /** Call this in your mouseUp event, after the lasso has been dragged. + + @see beginLasso, dragLasso + */ void endLasso() { source = 0; @@ -26128,12 +53549,23 @@ public: 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)); @@ -26147,6 +53579,7 @@ public: jassert (isMouseButtonDownAnywhere()); } + /** @internal */ bool hitTest (int x, int y) { return false; } juce_UseDebuggingNewOperator @@ -26175,23 +53608,65 @@ private: #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: @@ -26241,40 +53716,98 @@ class Component; class ComponentPeer; class MouseInputSourceInternal; +/** + Represents a linear source of mouse events from a mouse device or individual finger + in a multi-touch environment. + + Each MouseEvent object contains a reference to the MouseInputSource that generated + it. In an environment with a single mouse for input, all events will come from the + same source, but in a multi-touch system, there may be multiple MouseInputSource + obects active, each representing a stream of events coming from a particular finger. + + Events coming from a single MouseInputSource are always sent in a fixed and predictable + order: a mouseMove will never be called without a mouseEnter having been sent beforehand, + the only events that can happen between a mouseDown and its corresponding mouseUp are + mouseDrags, etc. + When there are multiple touches arriving from multiple MouseInputSources, their + event streams may arrive in an interleaved order, so you should use the getIndex() + method to find out which finger each event came from. + + @see MouseEvent +*/ class JUCE_API MouseInputSource { public: + /** Creates a MouseInputSource. + You should never actually create a MouseInputSource in your own code - the + library takes care of managing these objects. + */ MouseInputSource (int index, bool isMouseDevice); + /** Destructor. */ ~MouseInputSource(); + /** Returns true if this object represents a normal desk-based mouse device. */ bool isMouse() const; + /** Returns true if this object represents a source of touch events - i.e. a finger or stylus. */ bool isTouch() const; + /** Returns true if this source has an on-screen pointer that can hover over + items without clicking them. + */ bool canHover() const; + /** Returns true if this source may have a scroll wheel. */ bool hasMouseWheel() const; + /** Returns this source's index in the global list of possible sources. + If the system only has a single mouse, there will only be a single MouseInputSource + with an index of 0. + + If the system supports multi-touch input, then the index will represent a finger + number, starting from 0. When the first touch event begins, it will have finger + number 0, and then if a second touch happens while the first is still down, it + will have index 1, etc. + */ int getIndex() const; + /** Returns true if this device is currently being pressed. */ bool isDragging() const; + /** Returns the last-known screen position of this source. */ const Point getScreenPosition() const; + /** Returns a set of modifiers that indicate which buttons are currently + held down on this device. + */ const ModifierKeys getCurrentModifiers() const; + /** Returns the component that was last known to be under this pointer. */ Component* getComponentUnderMouse() const; + /** Tells the device to dispatch a mouse-move event. + This is asynchronous - the event will occur on the message thread. + */ void triggerFakeMove() const; + /** Returns the number of clicks that should be counted as belonging to the + current mouse event. + So the mouse is currently down and it's the second click of a double-click, this + will return 2. + */ int getNumberOfMultipleClicks() const throw(); + /** Returns the time at which the last mouse-down occurred. */ const Time getLastMouseDownTime() const throw(); + /** Returns the screen position at which the last mouse-down occurred. */ const Point getLastMouseDownPosition() const throw(); + /** Returns true if this mouse is currently down, and if it has been dragged more + than a couple of pixels from the place it was pressed. + */ bool hasMouseMovedSignificantlySincePressed() const throw(); bool hasMouseCursor() const throw(); @@ -26285,11 +53818,30 @@ public: bool canDoUnboundedMovement() const throw(); + /** Allows the mouse to move beyond the edges of the screen. + + Calling this method when the mouse button is currently pressed 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 isEnabled 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 isEnabled, bool keepCursorVisibleUntilOffscreen = false); juce_UseDebuggingNewOperator + /** @internal */ void handleEvent (ComponentPeer* peer, const Point& positionWithinPeer, int64 time, const ModifierKeys& mods); + /** @internal */ void handleWheel (ComponentPeer* peer, const Point& positionWithinPeer, int64 time, float x, float y); private: @@ -26319,28 +53871,57 @@ private: #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 { protected: + /** Creates a button component. + + If you use this constructor, you must override the getState() and setState() + methods. + + @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); public: + /** Creates a button component. + + @param valueToControl a Value object that this property should refer to. + @param propertyName the property name to be passed to the PropertyComponent + @param buttonText the text shown in the ToggleButton component + */ BooleanPropertyComponent (const Value& valueToControl, const String& propertyName, const String& buttonText); + /** Destructor. */ ~BooleanPropertyComponent(); + /** Called to change the state of the boolean value. */ virtual void setState (bool newState); + /** Must return the current value of the property. */ virtual bool getState() const; + /** @internal */ void paint (Graphics& g); + /** @internal */ void refresh(); + /** @internal */ void buttonClicked (Button*); juce_UseDebuggingNewOperator @@ -26366,21 +53947,43 @@ private: #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, 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 @@ -26403,32 +54006,88 @@ private: #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 { protected: + /** Creates the component. + + Your subclass's constructor must add a list of options to the choices + member variable. + */ ChoicePropertyComponent (const String& propertyName); public: + /** Creates the component. + + @param valueToControl the value that the combo box will read and control + @param propertyName the name of the property + @param choices the list of possible values that the user can choose between + @param choiceIDs if this is 0, then the value corresponding to each item in the + 'choices' StringArray is simply its index + 1. But if the + choiceIDs parameter is specified, it lets you provide a set + of IDs for each item in the choices list. If you use this + parameter, it must contain the same number of elements as + the choices list. + */ ChoicePropertyComponent (const Value& valueToControl, const String& propertyName, const StringArray& choices, const Array * choiceIDs = 0); + /** 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 (int newIndex); + /** 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; + /** Returns the list of options. */ const StringArray& getChoices() const; + /** @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: @@ -26457,11 +54116,23 @@ private: #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 { protected: + /** 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, double rangeMin, double rangeMax, @@ -26470,6 +54141,13 @@ protected: 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 Value& valueToControl, const String& propertyName, double rangeMin, @@ -26477,20 +54155,34 @@ public: double interval, 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 (double newValue); + /** Returns the value that the slider should show. */ virtual double getValue() const; + /** @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; SliderPropertyComponent (const SliderPropertyComponent&); @@ -26508,27 +54200,56 @@ protected: #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 { protected: + /** 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, int maxNumChars, bool isMultiLine); 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 Value& valueToControl, const String& propertyName, int maxNumChars, 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); + /** Returns the text that should be shown in the text editor. + */ virtual const String getText() const; + /** @internal */ void refresh(); + /** @internal */ void textWasEdited(); juce_UseDebuggingNewOperator @@ -26555,27 +54276,85 @@ private: #if JUCE_WINDOWS || 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 (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 @@ -26607,6 +54386,14 @@ private: #ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ #define __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ +/** + 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, @@ -26614,6 +54401,24 @@ class JUCE_API AudioDeviceSelectorComponent : public Component, { 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 + @param showChannelsAsStereoPairs if true, channels will be treated as pairs; if false, channels will be + treated as a set of separate mono channels. + @param hideAdvancedOptionsWithButton if true, only the minimum amount of UI components + are shown, with an "advanced" button that shows the rest of them + */ AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager, const int minAudioInputChannels, const int maxAudioInputChannels, @@ -26624,12 +54429,18 @@ public: const bool showChannelsAsStereoPairs, const bool hideAdvancedOptionsWithButton); + /** Destructor */ ~AudioDeviceSelectorComponent(); + /** @internal */ void resized(); + /** @internal */ void comboBoxChanged (ComboBox*); + /** @internal */ void buttonClicked (Button*); + /** @internal */ void changeListenerCallback (void*); + /** @internal */ void childBoundsChanged (Component*); juce_UseDebuggingNewOperator @@ -26665,15 +54476,40 @@ private: #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, @@ -26682,23 +54518,75 @@ public: 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 (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 (int arrowTipX, 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 @@ -26724,29 +54612,87 @@ private: #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 (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, int numMillisecondsBeforeRemoving, bool removeWhenMouseClicked = true, 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* component, const String& message, int numMillisecondsBeforeRemoving, bool removeWhenMouseClicked = true, bool deleteSelfAfterUse = false); + /** @internal */ void getContentSize (int& w, int& h); + /** @internal */ void paintContent (Graphics& g, int w, int h); + /** @internal */ void timerCallback(); juce_UseDebuggingNewOperator @@ -26776,12 +54722,21 @@ private: #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. */ @@ -26791,22 +54746,71 @@ public: 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 (int sectionsToShow = (showAlphaChannel | showColourAtTop | showSliders | showColourspace), int edgeGap = 4, 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 (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 (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. */ @@ -26862,25 +54866,64 @@ private: #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* contentComponent, bool deleteContentCompWhenNoLongerNeeded); + /** Destructor. */ ~MagnifierComponent(); + /** Returns the current content component. */ Component* getContentComponent() const { 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 { return scaleFactor; } + /** Changes the quality setting used to rescale the graphics. + */ void setResamplingQuality (Graphics::ResamplingQuality newQuality); juce_UseDebuggingNewOperator + /** @internal */ void childBoundsChanged (Component*); private: @@ -26919,6 +54962,23 @@ private: #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, @@ -26927,6 +54987,10 @@ class JUCE_API MidiKeyboardComponent : public Component, { public: + /** The direction of the keyboard. + + @see setOrientation + */ enum Orientation { horizontalKeyboard, @@ -26934,44 +54998,131 @@ public: 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, 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 (float velocity, bool useMousePositionForVelocity); + /** 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 (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 (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 (float widthInPixels); + /** Returns the width that was set by setKeyWidth(). */ float getKeyWidth() const throw() { return keyWidth; } + /** Changes the keyboard's current direction. */ void setOrientation (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 (int lowestNote, 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; } + /** Returns the length of the black notes. + + This will be their vertical or horizontal length, depending on the keyboard's orientation. + */ int getBlackNoteLength() const throw() { return blackNoteLength; } + /** 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 (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, @@ -26984,36 +55135,96 @@ public: 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, int midiNoteOffsetFromC); + /** Removes any key-mappings for a given note. + + For a description of what the note number means, see setKeyPressForNote(). + */ void removeKeyPressForNote (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 (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 (int octaveNumForMiddleC); + /** 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 (bool isKeyDown); + /** @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 @@ -27021,6 +55232,13 @@ public: 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, @@ -27028,23 +55246,60 @@ protected: 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; @@ -27098,18 +55353,44 @@ private: #if JUCE_MAC || DOXYGEN +/** + A Mac-specific class that can create and embed an NSView inside itself. + + To use it, create one of these, put it in place and make sure it's visible in a + window, then use setView() to assign an NSView to it. The view will then be + moved and resized to follow the movements of this component. + + Of course, since the view is a native object, it'll obliterate any + juce components that may overlap this component, but that's life. +*/ class JUCE_API NSViewComponent : public Component { public: + /** Create an initially-empty container. */ NSViewComponent(); + /** Destructor. */ ~NSViewComponent(); + /** Assigns an NSView to this peer. + + The view will be retained and released by this component for as long as + it is needed. To remove the current view, just call setView (0). + + Note: a void* is used here to avoid including the cocoa headers as + part of the juce.h, but the method expects an NSView*. + */ void setView (void* nsView); + /** Returns the current NSView. + + Note: a void* is returned here to avoid including the cocoa headers as + a requirement of juce.h, so you should just cast the object to an NSView*. + */ void* getView() const; + /** @internal */ void paint (Graphics& g); juce_UseDebuggingNewOperator @@ -27138,10 +55419,20 @@ private: // this is used to disable OpenGL, and is defined in juce_Config.h #if JUCE_OPENGL || DOXYGEN +/** + Represents the various properties of an OpenGL bitmap format. + + @see OpenGLComponent::setPixelFormat +*/ class JUCE_API OpenGLPixelFormat { public: + /** Creates an OpenGLPixelFormat. + + The default constructor just initialises the object as a simple 8-bit + RGBA format. + */ OpenGLPixelFormat (int bitsPerRGBComponent = 8, int alphaBits = 8, int depthBufferBits = 16, @@ -27166,36 +55457,79 @@ public: 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); juce_UseDebuggingNewOperator }; +/** + A base class for types of OpenGL context. + + An OpenGLComponent will supply its own context for drawing in its window. +*/ class JUCE_API 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 (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; + /** 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 @@ -27204,10 +55538,20 @@ 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: + /** Used to select the type of openGL API to use, if more than one choice is available + on a particular platform. + */ enum OpenGLType { openGLDefault = 0, @@ -27218,38 +55562,140 @@ public: #endif }; + /** Creates an OpenGLComponent. */ OpenGLComponent (OpenGLType type = openGLDefault); + /** 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); + /** Returns the context that this component is sharing with. + @see shareWith + */ OpenGLContext* getShareContext() const throw() { return 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() { return context; } + /** 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); + /** Returns the native handle of an embedded heavyweight window, if there is one. + + E.g. On windows, this will return the HWND of the sub-window containing + the opengl context, on the mac it'll be the NSOpenGLView. + */ void* getNativeWindowHandle() const; juce_UseDebuggingNewOperator @@ -27289,35 +55735,100 @@ private: #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, 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 @@ -27356,62 +55867,161 @@ private: // this is used to disable QuickTime, and is defined in juce_Config.h #if JUCE_QUICKTIME || DOXYGEN +/** + A window that can play back a QuickTime movie. + +*/ class JUCE_API QuickTimeMovieComponent : public QTCompBaseClass { 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 from a file 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, bool isControllerVisible); + /** Tries to load a QuickTime movie from a URL 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 movieURL 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 URL& movieURL, bool isControllerVisible); + /** Tries to load a QuickTime movie from a stream 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 movieStream a stream containing a .mov file. The component may try + to read the whole stream before playing, rather than + streaming from it. + @param isControllerVisible whether to show a controller bar at the bottom + @returns true if the movie opens successfully + */ bool loadMovie (InputStream* movieStream, 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 (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 (float newSpeed); + /** Changes the movie's playback volume. + + @param newVolume the volume in the range 0 (silent) to 1.0 (full) + */ void setMovieVolume (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 (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); juce_UseDebuggingNewOperator @@ -27452,19 +56062,40 @@ private: #if JUCE_WINDOWS || JUCE_LINUX || DOXYGEN +/** + 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 @@ -27494,31 +56125,77 @@ private: class WebBrowserComponentInternal; #endif +/** + 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 JUCE_API WebBrowserComponent : public Component { public: + /** Creates a WebBrowserComponent. + + Once it's created and visible, send the browser to a URL using goToURL(). + + @param unloadPageWhenBrowserIsHidden if this is true, then when the browser + component is taken offscreen, it'll clear the current page + and replace it with a blank page - this can be handy to stop + the browser using resources in the background when it's not + actually being used. + */ explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true); + /** 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(); + /** Refreshes the browser. + */ void refresh(); + /** 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 resized(); + /** @internal */ void parentHierarchyChanged(); + /** @internal */ void visibilityChanged(); juce_UseDebuggingNewOperator @@ -27554,10 +56231,20 @@ private: class ComponentBoundsConstrainer; +/** + 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: + /** A combination of these flags is passed to the ComponentPeer constructor. */ enum StyleFlags { windowAppearsOnTaskbar = (1 << 0), /**< Indicates that the window should have a corresponding @@ -27588,92 +56275,206 @@ public: }; + /** 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* component, int styleFlags); + /** 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, 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 const Rectangle getBounds() const = 0; + /** Returns the x-position of this window, relative to the screen's origin. */ virtual const Point getScreenPosition() const = 0; + /** Converts a position relative to the top-left of this component to screen co-ordinates. */ virtual const Point relativePositionToGlobal (const Point& relativePosition) = 0; + /** Converts a screen co-ordinate to a position relative to the top-left of this component. */ virtual const Point globalPositionToRelative (const Point& screenPosition) = 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* 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 (const Point& position, 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; + /** Tells the window that text input may be required at the given position. + + This may cause things like a virtual on-screen keyboard to appear, depending + on the OS. + */ virtual void textInputRequired (const Point& position) = 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 (int keyCode, juce_wchar textCharacter); + /** Called whenever a key is pressed or released. + Returns true if the keystroke was used. + */ bool handleKeyUpOrDown (bool isKeyDown); + /** Called whenever a modifier key is pressed or released. */ void handleModifierKeysChange(); + /** Returns the currently focused TextInputTarget, or null if none is found. */ TextInputTarget* findCurrentTextInputTarget(); + /** 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 handleMouseEvent (int touchIndex, const Point& positionWithinPeer, const ModifierKeys& newMods, int64 time); @@ -27685,14 +56486,42 @@ public: void handleFileDragExit (const StringArray& files); void handleFileDragDrop (const StringArray& files, const Point& position); + /** Resets the masking region. + + The subclass should call this every time it's about to call the handlePaint + method. + + @see addMaskedRegion + */ void clearMaskedRegion(); + /** 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); + /** 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 (int index) throw(); + /** Checks if this peer object is valid. + + @see getNumPeers + */ static bool isValidPeer (const ComponentPeer* peer) throw(); static void bringModalComponentToFront(); @@ -27739,17 +56568,86 @@ private: #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, bool escapeKeyTriggersCloseButton, 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, @@ -27761,6 +56659,7 @@ public: juce_UseDebuggingNewOperator protected: + /** @internal */ void resized(); private: @@ -27787,22 +56686,97 @@ private: #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 ("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* backgroundImage, int minimumTimeToDisplayFor, bool useDropShadow, 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, int width, int height, @@ -27810,7 +56784,9 @@ public: bool useDropShadow, bool removeOnMouseClick = true); + /** @internal */ void paint (Graphics& g); + /** @internal */ void timerCallback(); juce_UseDebuggingNewOperator @@ -27835,25 +56811,115 @@ private: #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 ("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() ) + @param cancelButtonText the text that should be shown in the cancel button + (if it has one) + */ ThreadWithProgressWindow (const String& windowTitle, bool hasProgressBar, bool hasCancelButton, int timeOutMsWhenCancelling = 10000, const String& cancelButtonText = "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 (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 (double newProgress); + /** The thread can call this to change the message that's displayed in the dialog box. + */ void setStatusMessage (const String& newStatusMessage); + /** Returns the AlertWindow that is being used. + */ AlertWindow* getAlertWindow() const throw() { return alertWindow; } juce_UseDebuggingNewOperator @@ -27912,6 +56978,19 @@ private: #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: @@ -27921,8 +57000,14 @@ protected: 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; virtual bool clipToRectangle (const Rectangle& r) = 0; @@ -27968,6 +57053,11 @@ public: #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: @@ -28063,6 +57153,13 @@ protected: class LLGCSavedState; +/** + 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: @@ -28137,37 +57234,115 @@ protected: #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, 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, int index = -1); + /** Deletes one of the Drawable objects. + + @param index the index of the drawable to delete, between 0 + and (getNumDrawables() - 1). + @param deleteDrawable if this is true, the drawable that is removed will also + be deleted. If false, it'll just be removed. + @see insertDrawable, getNumDrawables + */ void removeDrawable (int index, bool deleteDrawable = true); + /** 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 (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 (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 (int index); + /** @internal */ void render (const Drawable::RenderingContext& context) const; + /** @internal */ const Rectangle getBounds() const; + /** @internal */ bool hitTest (float x, float y) const; + /** @internal */ Drawable* createCopy() const; + /** @internal */ ValueTree createValueTree() const; + /** @internal */ static DrawableComposite* createFromValueTree (const ValueTree& tree); juce_UseDebuggingNewOperator @@ -28191,36 +57366,83 @@ private: #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, 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 (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 render (const Drawable::RenderingContext& context) const; + /** @internal */ const Rectangle getBounds() const; + /** @internal */ bool hitTest (float x, float y) const; + /** @internal */ Drawable* createCopy() const; + /** @internal */ ValueTree createValueTree() const; + /** @internal */ static DrawableImage* createFromValueTree (const ValueTree& tree); juce_UseDebuggingNewOperator @@ -28246,37 +57468,81 @@ private: #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 setFillColour, setStrokeType + */ void setPath (const Path& newPath); + /** Returns the current path. */ const Path& getPath() const throw() { return path; } + /** Sets a fill type for the path. + + 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 to a transparent + colour. + + @see setPath, setStrokeFill + */ void setFill (const FillType& newFill); + /** Returns the current fill type. + @see setFill + */ const FillType& getFill() const throw() { return mainFill; } + /** Sets the fill type with which the outline will be drawn. + @see setFill + */ void setStrokeFill (const FillType& newStrokeFill); + /** Returns the current stroke fill. + @see setStrokeFill + */ const FillType& getStrokeFill() const throw() { return strokeFill; } + /** Changes the properties of the outline that will be drawn around the path. + If the stroke has 0 thickness, no stroke will be drawn. + @see setStrokeThickness, setStrokeColour + */ void setStrokeType (const PathStrokeType& newStrokeType); + /** Changes the stroke thickness. + This is a shortcut for calling setStrokeType. + */ void setStrokeThickness (float newThickness); + /** Returns the current outline style. */ const PathStrokeType& getStrokeType() const throw() { return strokeType; } + /** @internal */ void render (const Drawable::RenderingContext& context) const; + /** @internal */ const Rectangle getBounds() const; + /** @internal */ bool hitTest (float x, float y) const; + /** @internal */ Drawable* createCopy() const; + /** @internal */ ValueTree createValueTree() const; + /** @internal */ static DrawablePath* createFromValueTree (const ValueTree& tree); juce_UseDebuggingNewOperator @@ -28303,29 +57569,52 @@ private: #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 render (const Drawable::RenderingContext& context) const; + /** @internal */ const Rectangle getBounds() const; + /** @internal */ bool hitTest (float x, float y) const; + /** @internal */ Drawable* createCopy() const; + /** @internal */ ValueTree createValueTree() const; + /** @internal */ static DrawableText* createFromValueTree (const ValueTree& tree); juce_UseDebuggingNewOperator @@ -28352,17 +57641,36 @@ private: #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 (float newRadius, const Colour& newColour); + /** @internal */ void applyEffect (Image& sourceImage, Graphics& destContext); juce_UseDebuggingNewOperator @@ -28386,16 +57694,35 @@ private: #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 (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 (float newOpacity); + /** @internal */ void applyEffect (Image& sourceImage, Graphics& destContext); juce_UseDebuggingNewOperator @@ -28439,27 +57766,70 @@ private: #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 = 6.0f); + /** Destructor. */ ~PathFlatteningIterator(); + /** 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(); + /** 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 { return stackPos == stackBase.getData() && (index >= path.numElements || points [index] == Path::moveMarker); } @@ -28498,40 +57868,156 @@ private: #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. */ 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 (double x, double y, double width, 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. */ @@ -28539,6 +58025,7 @@ public: 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. */ @@ -28547,6 +58034,7 @@ public: 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. */ @@ -28554,6 +58042,12 @@ public: 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, @@ -28562,38 +58056,101 @@ public: 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. */ bool operator== (const PositionedRectangle& other) const throw(); + /** Compares two objects. */ bool operator!= (const PositionedRectangle& other) const throw(); juce_UseDebuggingNewOperator @@ -28633,45 +58190,114 @@ private: #if JUCE_USE_CAMERA +/** + Receives callbacks with images from a CameraDevice. + + @see CameraDevice::addListener +*/ class CameraImageListener { public: CameraImageListener() {} virtual ~CameraImageListener() {} + /** This method is called when a new image arrives. + + This may be called by any thread, so be careful about thread-safety, + and make sure that you process the data as quickly as possible to + avoid glitching! + */ virtual void imageReceived (Image& image) = 0; }; +/** + Controls any camera capture devices that might be available. + + Use getAvailableDevices() to list the devices that are attached to the + system, then call openDevice to open one for use. Once you have a CameraDevice + object, you can get a viewer component from it, and use its methods to + stream to a file or capture still-frames. +*/ class JUCE_API CameraDevice { public: + /** Destructor. */ virtual ~CameraDevice(); + /** Returns a list of the available cameras on this machine. + + You can open one of these devices by calling openDevice(). + */ static const StringArray getAvailableDevices(); + /** Opens a camera device. + + The index parameter indicates which of the items returned by getAvailableDevices() + to open. + + The size constraints allow the method to choose between different resolutions if + the camera supports this. If the resolution cam't be specified (e.g. on the Mac) + then these will be ignored. + */ static CameraDevice* openDevice (int deviceIndex, int minWidth = 128, int minHeight = 64, int maxWidth = 1024, int maxHeight = 768); + /** Returns the name of this device */ const String getName() const { return name; } + /** Creates a component that can be used to display a preview of the + video from this camera. + */ Component* createViewerComponent(); + /** Starts recording video to the specified file. + + You should use getFileExtension() to find out the correct extension to + use for your filename. + + If the file exists, it will be deleted before the recording starts. + + This method may not start recording instantly, so if you need to know the + exact time at which the file begins, you can call getTimeOfFirstRecordedFrame() + after the recording has finished. + + The quality parameter can be 0, 1, or 2, to indicate low, medium, or high. It may + or may not be used, depending on the driver. + */ void startRecordingToFile (const File& file, int quality = 2); + /** Stops recording, after a call to startRecordingToFile(). + */ void stopRecording(); + /** Returns the file extension that should be used for the files + that you pass to startRecordingToFile(). + + This may be platform-specific, e.g. ".mov" or ".avi". + */ static const String getFileExtension(); + /** After calling stopRecording(), this method can be called to return the timestamp + of the first frame that was written to the file. + */ const Time getTimeOfFirstRecordedFrame() const; + /** Adds a listener to receive images from the camera. + + Be very careful not to delete the listener without first removing it by calling + removeListener(). + */ void addListener (CameraImageListener* listenerToAdd); + /** Removes a listener that was previously added with addListener(). + */ void removeListener (CameraImageListener* listenerToRemove); juce_UseDebuggingNewOperator protected: + /** @internal */ CameraDevice (const String& name, int index); private: @@ -28698,27 +58324,123 @@ private: #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, 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* imageToRelease); + /** Releases an image if it's in the cache, or deletes it if it isn't cached. + + This is a handy function to use if you want to delete an image but are afraid that + it might be cached. + */ static void releaseOrDelete (Image* 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* 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* 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 (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* image, 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 (int millisecs); juce_UseDebuggingNewOperator @@ -28753,28 +58475,72 @@ private: #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 (int size); + /** Destructor. */ ~ImageConvolutionKernel(); + /** Resets all values in the kernel to zero. */ void clear(); + /** Returns one of the kernel values. */ float getKernelValue (int x, int y) const 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 (int x, int y, 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 (float desiredTotalSum); + /** Multiplies all values in the kernel by a value. */ void rescaleAllValues (float multiplier); + /** 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 (float blurRadius); + /** Returns the size of the kernel. + + E.g. if it's a 3x3 kernel, this returns 3. + */ int getKernelSize() const { return size; } + /** 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 destinationArea the region of the image to apply the filter to + */ void applyToImage (Image& destImage, const Image* sourceImage, const Rectangle& destinationArea) const; @@ -28801,35 +58567,114 @@ private: #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() {} public: + /** Destructor. */ virtual ~ImageFileFormat() {} + /** 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 JUCE_API PNGImageFormat : public ImageFileFormat { public: @@ -28845,6 +58690,11 @@ public: bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); }; +/** + A type of ImageFileFormat for reading and writing JPEG files. + + @see ImageFileFormat, PNGImageFormat +*/ class JUCE_API JPEGImageFormat : public ImageFileFormat { public: @@ -28852,6 +58702,11 @@ public: JPEGImageFormat(); ~JPEGImageFormat(); + /** 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(); @@ -28880,27 +58735,98 @@ private: #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 { 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 (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, 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 (bool showMessageOnFailure); + /** A set of possible outcomes of one of the save() methods + */ enum SaveResult { savedOk = 0, /**< indicates that a file was saved successfully. */ @@ -28908,32 +58834,147 @@ public: 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 (bool askUserForFileIfNotSpecified, 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, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, 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 (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 { 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: @@ -28964,38 +59005,116 @@ private: #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 (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 (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, int baseItemId, bool showFullPaths, 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 @@ -29020,11 +59139,20 @@ private: #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); + /** Gets the current clipboard's contents. + + Obviously this might have come from another app, so could contain + anything.. + */ static const String getTextFromClipboard(); }; @@ -29060,6 +59188,14 @@ END_JUCE_NAMESPACE // 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_IPHONE) && ! JUCE_DONT_DEFINE_MACROS #define Component JUCE_NAMESPACE::Component #define MemoryBlock JUCE_NAMESPACE::MemoryBlock @@ -29067,16 +59203,32 @@ END_JUCE_NAMESPACE #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_WINDOWS && ! 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 #if JUCE_DEBUG #define AUTOLINKEDLIB "JUCE_debug.lib" @@ -29147,6 +59299,13 @@ END_JUCE_NAMESPACE #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) \ diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 14441e76b5..801aa67d92 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 1 +#define JUCE_BUILDNUMBER 2 /** Current Juce version number.